summaryrefslogtreecommitdiff
path: root/compiler/GHC/StgToJS
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/StgToJS')
-rw-r--r--compiler/GHC/StgToJS/Apply.hs1152
-rw-r--r--compiler/GHC/StgToJS/Arg.hs285
-rw-r--r--compiler/GHC/StgToJS/Closure.hs156
-rw-r--r--compiler/GHC/StgToJS/CodeGen.hs367
-rw-r--r--compiler/GHC/StgToJS/CoreUtils.hs282
-rw-r--r--compiler/GHC/StgToJS/DataCon.hs124
-rw-r--r--compiler/GHC/StgToJS/Deps.hs191
-rw-r--r--compiler/GHC/StgToJS/Expr.hs1045
-rw-r--r--compiler/GHC/StgToJS/ExprCtx.hs172
-rw-r--r--compiler/GHC/StgToJS/FFI.hs352
-rw-r--r--compiler/GHC/StgToJS/Heap.hs155
-rw-r--r--compiler/GHC/StgToJS/Ids.hs238
-rw-r--r--compiler/GHC/StgToJS/Linker/Linker.hs953
-rw-r--r--compiler/GHC/StgToJS/Linker/Types.hs101
-rw-r--r--compiler/GHC/StgToJS/Linker/Utils.hs308
-rw-r--r--compiler/GHC/StgToJS/Literal.hs109
-rw-r--r--compiler/GHC/StgToJS/Monad.hs183
-rw-r--r--compiler/GHC/StgToJS/Object.hs622
-rw-r--r--compiler/GHC/StgToJS/Prim.hs1509
-rw-r--r--compiler/GHC/StgToJS/Printer.hs218
-rw-r--r--compiler/GHC/StgToJS/Profiling.hs178
-rw-r--r--compiler/GHC/StgToJS/Regs.hs142
-rw-r--r--compiler/GHC/StgToJS/Rts/Rts.hs661
-rw-r--r--compiler/GHC/StgToJS/Rts/Types.hs78
-rw-r--r--compiler/GHC/StgToJS/Sinker.hs180
-rw-r--r--compiler/GHC/StgToJS/Stack.hs373
-rw-r--r--compiler/GHC/StgToJS/StaticPtr.hs28
-rw-r--r--compiler/GHC/StgToJS/StgUtils.hs266
-rw-r--r--compiler/GHC/StgToJS/Symbols.hs89
-rw-r--r--compiler/GHC/StgToJS/Types.hs430
-rw-r--r--compiler/GHC/StgToJS/Utils.hs57
31 files changed, 11004 insertions, 0 deletions
diff --git a/compiler/GHC/StgToJS/Apply.hs b/compiler/GHC/StgToJS/Apply.hs
new file mode 100644
index 0000000000..6d40f8a7ac
--- /dev/null
+++ b/compiler/GHC/StgToJS/Apply.hs
@@ -0,0 +1,1152 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BlockArguments #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Apply
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+--
+-- Module that deals with expression application in JavaScript. In some cases we
+-- rely on pre-generated functions that are bundled with the RTS (see rtsApply).
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Apply
+ ( genApp
+ , rtsApply
+ )
+where
+
+import GHC.Prelude hiding ((.|.))
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Arg
+import GHC.StgToJS.Closure
+import GHC.StgToJS.DataCon
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Types
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Regs
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Utils
+import GHC.StgToJS.Rts.Types
+import GHC.StgToJS.Stack
+import GHC.StgToJS.Ids
+
+import GHC.Types.Literal
+import GHC.Types.Id
+import GHC.Types.Id.Info
+import GHC.Types.CostCentre
+
+import GHC.Stg.Syntax
+
+import GHC.Builtin.Names
+
+import GHC.Core.TyCon
+import GHC.Core.DataCon
+import GHC.Core.Type hiding (typeSize)
+
+import GHC.Utils.Encoding
+import GHC.Utils.Misc
+import GHC.Utils.Monad
+import GHC.Utils.Panic
+import GHC.Utils.Outputable (vcat, ppr)
+import GHC.Data.FastString
+
+import qualified Data.Bits as Bits
+import Data.Monoid
+import Data.Array
+
+-- | Pre-generated functions for fast Apply.
+-- These are bundled with the RTS.
+rtsApply :: StgToJSConfig -> JStat
+rtsApply cfg = BlockStat $
+ map (specApply cfg) applySpec
+ ++ map (pap cfg) specPap
+ ++ [ mkApplyArr
+ , genericStackApply cfg
+ , genericFastApply cfg
+ , zeroApply cfg
+ , updates cfg
+ , papGen cfg
+ , moveRegs2
+ , selectors cfg
+ ]
+
+
+-- | Generate an application of some args to an Id.
+--
+-- The case where args is null is common as it's used to generate the evaluation
+-- code for an Id.
+genApp
+ :: HasDebugCallStack
+ => ExprCtx
+ -> Id
+ -> [StgArg]
+ -> G (JStat, ExprResult)
+genApp ctx i args
+
+ -- Case: unpackCStringAppend# "some string"# str
+ --
+ -- Generates h$appendToHsStringA(str, "some string"), which has a faster
+ -- decoding loop.
+ | [StgLitArg (LitString bs), x] <- args
+ , [top] <- concatMap typex_expr (ctxTarget ctx)
+ , getUnique i == unpackCStringAppendIdKey
+ , d <- utf8DecodeByteString bs
+ = do
+ prof <- csProf <$> getSettings
+ let profArg = if prof then [jCafCCS] else []
+ a <- genArg x
+ return ( top |= app "h$appendToHsStringA" ([toJExpr d, toJExpr a] ++ profArg)
+ , ExprInline Nothing
+ )
+
+ -- let-no-escape
+ | Just n <- ctxLneBindingStackSize ctx i
+ = do
+ as' <- concatMapM genArg args
+ ei <- varForEntryId i
+ let ra = mconcat . reverse $
+ zipWith (\r a -> toJExpr r |= a) [R1 ..] as'
+ p <- pushLneFrame n ctx
+ a <- adjSp 1 -- for the header (which will only be written when the thread is suspended)
+ return (ra <> p <> a <> returnS ei, ExprCont)
+
+ -- proxy#
+ | [] <- args
+ , getUnique i == proxyHashKey
+ , [top] <- concatMap typex_expr (ctxTarget ctx)
+ = return (top |= null_, ExprInline Nothing)
+
+ -- unboxed tuple or strict type: return fields individually
+ | [] <- args
+ , isUnboxedTupleType (idType i) || isStrictType (idType i)
+ = do
+ a <- storeIdFields i (ctxTarget ctx)
+ return (a, ExprInline Nothing)
+
+ -- Handle alternative heap object representation: in some cases, a heap
+ -- object is not represented as a JS object but directly as a number or a
+ -- string. I.e. only the payload is stored because the box isn't useful.
+ -- It happens for "Int Int#" for example: no need to box the Int# in JS.
+ --
+ -- We must check that:
+ -- - the object is subject to the optimization (cf isUnboxable predicate)
+ -- - we know that it is already evaluated (cf ctxIsEvaluated), otherwise we
+ -- need to evaluate it properly first.
+ --
+ -- In which case we generate a dynamic check (using isObject) that either:
+ -- - returns the payload of the heap object, if it uses the generic heap
+ -- object representation
+ -- - returns the object directly, otherwise
+ | [] <- args
+ , [vt] <- idVt i
+ , isUnboxable vt
+ , ctxIsEvaluated ctx i
+ = do
+ let c = head (concatMap typex_expr $ ctxTarget ctx)
+ is <- varsForId i
+ case is of
+ [i'] ->
+ return ( c |= if_ (isObject i') (closureField1 i') i'
+ , ExprInline Nothing
+ )
+ _ -> panic "genApp: invalid size"
+
+ -- case of Id without args and known to be already evaluated: return fields
+ -- individually
+ | [] <- args
+ , ctxIsEvaluated ctx i || isStrictType (idType i)
+ = do
+ a <- storeIdFields i (ctxTarget ctx)
+ -- optional runtime assert for detecting unexpected thunks (unevaluated)
+ settings <- getSettings
+ let ww = case concatMap typex_expr (ctxTarget ctx) of
+ [t] | csAssertRts settings ->
+ ifS (isObject t .&&. isThunk t)
+ (appS "throw" [String "unexpected thunk"]) -- yuck
+ mempty
+ _ -> mempty
+ return (a `mappend` ww, ExprInline Nothing)
+
+
+ -- Case: "newtype" datacon wrapper
+ --
+ -- If the wrapped argument is known to be already evaluated, then we don't
+ -- need to enter it.
+ | DataConWrapId dc <- idDetails i
+ , isNewTyCon (dataConTyCon dc)
+ = do
+ as <- concatMapM genArg args
+ case as of
+ [ai] -> do
+ let t = head (concatMap typex_expr (ctxTarget ctx))
+ a' = case args of
+ [StgVarArg a'] -> a'
+ _ -> panic "genApp: unexpected arg"
+ if isStrictId a' || ctxIsEvaluated ctx a'
+ then return (t |= ai, ExprInline Nothing)
+ else return (returnS (app "h$e" [ai]), ExprCont)
+ _ -> panic "genApp: invalid size"
+
+ -- no args and Id can't be a function: just enter it
+ | [] <- args
+ , idFunRepArity i == 0
+ , not (might_be_a_function (idType i))
+ = do
+ enter_id <- genIdArg i >>=
+ \case
+ [x] -> return x
+ xs -> pprPanic "genApp: unexpected multi-var argument"
+ (vcat [ppr (length xs), ppr i])
+ return (returnS (app "h$e" [enter_id]), ExprCont)
+
+ -- fully saturated global function:
+ -- - deals with arguments
+ -- - jumps into the function
+ | n <- length args
+ , n /= 0
+ , idFunRepArity i == n
+ , not (isLocalId i)
+ , isStrictId i
+ = do
+ as' <- concatMapM genArg args
+ is <- assignAll jsRegsFromR1 <$> varsForId i
+ jmp <- jumpToII i as' is
+ return (jmp, ExprCont)
+
+ -- oversaturated function:
+ -- - push continuation with extra args
+ -- - deals with arguments
+ -- - jumps into the function
+ | idFunRepArity i < length args
+ , isStrictId i
+ , idFunRepArity i > 0
+ = do
+ let (reg,over) = splitAt (idFunRepArity i) args
+ reg' <- concatMapM genArg reg
+ pc <- pushCont over
+ is <- assignAll jsRegsFromR1 <$> varsForId i
+ jmp <- jumpToII i reg' is
+ return (pc <> jmp, ExprCont)
+
+ -- generic apply:
+ -- - try to find a pre-generated apply function that matches
+ -- - use it if any
+ -- - otherwise use generic apply function h$ap_gen_fast
+ | otherwise
+ = do
+ is <- assignAll jsRegsFromR1 <$> varsForId i
+ jmp <- jumpToFast args is
+ return (jmp, ExprCont)
+
+-- avoid one indirection for global ids
+-- fixme in many cases we can also jump directly to the entry for local?
+jumpToII :: Id -> [JExpr] -> JStat -> G JStat
+jumpToII i vars load_app_in_r1
+ | isLocalId i = do
+ ii <- varForId i
+ return $ mconcat
+ [ assignAllReverseOrder jsRegsFromR2 vars
+ , load_app_in_r1
+ , returnS (closureEntry ii)
+ ]
+ | otherwise = do
+ ei <- varForEntryId i
+ return $ mconcat
+ [ assignAllReverseOrder jsRegsFromR2 vars
+ , load_app_in_r1
+ , returnS ei
+ ]
+
+-- | Try to use a specialized pre-generated application function.
+-- If there is none, use h$ap_gen_fast instead
+jumpToFast :: HasDebugCallStack => [StgArg] -> JStat -> G JStat
+jumpToFast args load_app_in_r1 = do
+ -- get JS expressions for every argument
+ -- Arguments may have more than one expression (e.g. Word64#)
+ vars <- concatMapM genArg args
+ -- try to find a specialized apply function
+ let spec = mkApplySpec RegsConv args vars
+ ap_fun <- selectApply spec
+ pure $ mconcat
+ [ assignAllReverseOrder jsRegsFromR2 vars
+ , load_app_in_r1
+ , case ap_fun of
+ -- specialized apply: no tag
+ Right fun -> returnS (ApplExpr fun [])
+ -- generic apply: pass a tag indicating number of args/slots
+ Left fun -> returnS (ApplExpr fun [specTagExpr spec])
+ ]
+
+-- | Calling convention for an apply function
+data ApplyConv
+ = RegsConv -- ^ Fast calling convention: use registers
+ | StackConv -- ^ Slow calling convention: use the stack
+ deriving (Show,Eq,Ord)
+
+-- | Name of the generic apply function
+genericApplyName :: ApplyConv -> FastString
+genericApplyName = \case
+ RegsConv -> "h$ap_gen_fast"
+ StackConv -> "h$ap_gen"
+
+-- | Expr of the generic apply function
+genericApplyExpr :: ApplyConv -> JExpr
+genericApplyExpr conv = var (genericApplyName conv)
+
+
+-- | Return the name of the specialized apply function for the given number of
+-- args, number of arg variables, and calling convention.
+specApplyName :: ApplySpec -> FastString
+specApplyName = \case
+ -- specialize a few for compiler performance (avoid building FastStrings over
+ -- and over for common cases)
+ ApplySpec RegsConv 0 0 -> "h$ap_0_0_fast"
+ ApplySpec StackConv 0 0 -> "h$ap_0_0"
+ ApplySpec RegsConv 1 0 -> "h$ap_1_0_fast"
+ ApplySpec StackConv 1 0 -> "h$ap_1_0"
+ ApplySpec RegsConv 1 1 -> "h$ap_1_1_fast"
+ ApplySpec StackConv 1 1 -> "h$ap_1_1"
+ ApplySpec RegsConv 1 2 -> "h$ap_1_2_fast"
+ ApplySpec StackConv 1 2 -> "h$ap_1_2"
+ ApplySpec RegsConv 2 1 -> "h$ap_2_1_fast"
+ ApplySpec StackConv 2 1 -> "h$ap_2_1"
+ ApplySpec RegsConv 2 2 -> "h$ap_2_2_fast"
+ ApplySpec StackConv 2 2 -> "h$ap_2_2"
+ ApplySpec RegsConv 2 3 -> "h$ap_2_3_fast"
+ ApplySpec StackConv 2 3 -> "h$ap_2_3"
+ ApplySpec conv nargs nvars -> mkFastString $ mconcat
+ [ "h$ap_", show nargs
+ , "_" , show nvars
+ , case conv of
+ RegsConv -> "_fast"
+ StackConv -> ""
+ ]
+
+-- | Return the expression of the specialized apply function for the given
+-- number of args, number of arg variables, and calling convention.
+--
+-- Warning: the returned function may not be generated! Use specApplyExprMaybe
+-- if you want to ensure that it exists.
+specApplyExpr :: ApplySpec -> JExpr
+specApplyExpr spec = var (specApplyName spec)
+
+-- | Return the expression of the specialized apply function for the given
+-- number of args, number of arg variables, and calling convention.
+-- Return Nothing if it isn't generated.
+specApplyExprMaybe :: ApplySpec -> Maybe JExpr
+specApplyExprMaybe spec =
+ if spec `elem` applySpec
+ then Just (specApplyExpr spec)
+ else Nothing
+
+-- | Make an ApplySpec from a calling convention, a list of Haskell args, and a
+-- list of corresponding JS variables
+mkApplySpec :: ApplyConv -> [StgArg] -> [JExpr] -> ApplySpec
+mkApplySpec conv args vars = ApplySpec
+ { specConv = conv
+ , specArgs = length args
+ , specVars = length vars
+ }
+
+-- | Find a specialized application function if there is one
+selectApply
+ :: ApplySpec
+ -> G (Either JExpr JExpr) -- ^ the function to call (Left for generic, Right for specialized)
+selectApply spec =
+ case specApplyExprMaybe spec of
+ Just e -> return (Right e)
+ Nothing -> return (Left (genericApplyExpr (specConv spec)))
+
+
+-- | Apply specification
+data ApplySpec = ApplySpec
+ { specConv :: !ApplyConv -- ^ Calling convention
+ , specArgs :: !Int -- ^ number of Haskell arguments
+ , specVars :: !Int -- ^ number of JavaScript variables for the arguments
+ }
+ deriving (Show,Eq,Ord)
+
+-- | List of specialized apply function templates
+applySpec :: [ApplySpec]
+applySpec = [ ApplySpec conv nargs nvars
+ | conv <- [RegsConv, StackConv]
+ , nargs <- [0..4]
+ , nvars <- [max 0 (nargs-1)..(nargs*2)]
+ ]
+
+-- | Generate a tag for the given ApplySpec
+--
+-- Warning: tag doesn't take into account the calling convention
+specTag :: ApplySpec -> Int
+specTag spec = Bits.shiftL (specVars spec) 8 Bits..|. (specArgs spec)
+
+-- | Generate a tag expression for the given ApplySpec
+specTagExpr :: ApplySpec -> JExpr
+specTagExpr = toJExpr . specTag
+
+-- | Build arrays to quickly lookup apply functions
+--
+-- h$apply[r << 8 | n] = function application for r regs, n args
+-- h$paps[r] = partial application for r registers (number of args is in the object)
+mkApplyArr :: JStat
+mkApplyArr = mconcat
+ [ TxtI "h$apply" ||= toJExpr (JList [])
+ , TxtI "h$paps" ||= toJExpr (JList [])
+ , ApplStat (var "h$initStatic" .^ "push")
+ [ ValExpr $ JFunc [] $ jVar \i -> mconcat
+ [ i |= zero_
+ , WhileStat False (i .<. Int 65536) $ mconcat
+ [ var "h$apply" .! i |= var "h$ap_gen"
+ , preIncrS i
+ ]
+ , i |= zero_
+ , WhileStat False (i .<. Int 128) $ mconcat
+ [ var "h$paps" .! i |= var "h$pap_gen"
+ , preIncrS i
+ ]
+ , mconcat (map assignSpec applySpec)
+ , mconcat (map assignPap specPap)
+ ]
+ ]
+ ]
+ where
+ assignSpec :: ApplySpec -> JStat
+ assignSpec spec = case specConv spec of
+ -- both fast/slow (regs/stack) specialized apply functions have the same
+ -- tags. We store the stack ones in the array because they are used as
+ -- continuation stack frames.
+ StackConv -> var "h$apply" .! specTagExpr spec |= specApplyExpr spec
+ RegsConv -> mempty
+
+ assignPap :: Int -> JStat
+ assignPap p = var "h$paps" .! toJExpr p |=
+ (var (mkFastString $ ("h$pap_" ++ show p)))
+
+-- | Push a continuation on the stack
+--
+-- First push the given args, then push an apply function (specialized if
+-- possible, otherwise the generic h$ap_gen function).
+pushCont :: HasDebugCallStack
+ => [StgArg]
+ -> G JStat
+pushCont args = do
+ vars <- concatMapM genArg args
+ let spec = mkApplySpec StackConv args vars
+ selectApply spec >>= \case
+ Right app -> push $ reverse $ app : vars
+ Left app -> push $ reverse $ app : specTagExpr spec : vars
+
+-- | Generic stack apply function (h$ap_gen) that can do everything, but less
+-- efficiently than other more specialized functions.
+--
+-- Stack layout:
+-- -3: ...
+-- -2: args
+-- -1: tag (number of arg slots << 8 | number of args)
+--
+-- Regs:
+-- R1 = applied closure
+--
+genericStackApply :: StgToJSConfig -> JStat
+genericStackApply cfg = closure info body
+ where
+ -- h$ap_gen body
+ body = jVar \cf ->
+ [ traceRts cfg (jString "h$ap_gen")
+ , cf |= closureEntry r1
+ -- switch on closure type
+ , SwitchStat (entryClosureType cf)
+ [ (toJExpr Thunk , thunk_case cfg cf)
+ , (toJExpr Fun , fun_case cf (funArity' cf))
+ , (toJExpr Pap , fun_case cf (papArity r1))
+ , (toJExpr Blackhole, blackhole_case cfg)
+ ]
+ (default_case cf)
+ ]
+
+ -- info table for h$ap_gen
+ info = ClosureInfo
+ { ciVar = TxtI "h$ap_gen"
+ , ciRegs = CIRegs 0 [PtrV] -- closure to apply to
+ , ciName = "h$ap_gen"
+ , ciLayout = CILayoutVariable
+ , ciType = CIStackFrame
+ , ciStatic = mempty
+ }
+
+ default_case cf = appS "throw" [jString "h$ap_gen: unexpected closure type "
+ + (entryClosureType cf)]
+
+ thunk_case cfg cf = mconcat
+ [ profStat cfg pushRestoreCCS
+ , returnS cf
+ ]
+
+ blackhole_case cfg = mconcat
+ [ push' cfg [r1, var "h$return"]
+ , returnS (app "h$blockOnBlackhole" [r1])
+ ]
+
+ fun_case c arity = jVar \tag needed_args needed_regs given_args given_regs newTag newAp p dat ->
+ [ tag |= stack .! (sp - 1) -- tag on the stack
+ , given_args |= mask8 tag -- indicates the number of passed args
+ , given_regs |= tag .>>. 8 -- and the number of passed values for registers
+ , needed_args |= mask8 arity
+ , needed_regs |= arity .>>. 8
+ , traceRts cfg (jString "h$ap_gen: args: " + given_args
+ + jString " regs: " + given_regs)
+ , ifBlockS (given_args .===. needed_args)
+ --------------------------------
+ -- exactly saturated application
+ --------------------------------
+ [ traceRts cfg (jString "h$ap_gen: exact")
+ -- Set registers to register values on the stack
+ , loop 0 (.<. given_regs) \i -> mconcat
+ [ appS "h$setReg" [i+2, stack .! (sp-2-i)]
+ , postIncrS i
+ ]
+ -- drop register values from the stack
+ , sp |= sp - given_regs - 2
+ -- enter closure in R1
+ , returnS c
+ ]
+ [ ifBlockS (given_args .>. needed_args)
+ ----------------------------
+ -- oversaturated application
+ ----------------------------
+ [ traceRts cfg (jString "h$ap_gen: oversat: arity: " + needed_args
+ + jString " regs: " + needed_regs)
+ -- load needed register values
+ , loop 0 (.<. needed_regs) \i -> mconcat
+ [ traceRts cfg (jString "h$ap_gen: loading register: " + i)
+ , appS "h$setReg" [i+2, stack .! (sp-2-i)]
+ , postIncrS i
+ ]
+ -- compute new tag with consumed register values and args removed
+ , newTag |= ((given_regs-needed_regs).<<.8) .|. (given_args - needed_args)
+ -- find application function for the remaining regs/args
+ , newAp |= var "h$apply" .! newTag
+ , traceRts cfg (jString "h$ap_gen: next: " + (newAp .^ "n"))
+
+ -- Drop used registers from the stack.
+ -- Test if the application function needs a tag and push it.
+ , ifS (newAp .===. var "h$ap_gen")
+ ((sp |= sp - needed_regs) <> (stack .! (sp - 1) |= newTag))
+ (sp |= sp - needed_regs - 1)
+
+ -- Push generic application function as continuation
+ , stack .! sp |= newAp
+
+ -- Push "current thread CCS restore" function as continuation
+ , profStat cfg pushRestoreCCS
+
+ -- enter closure in R1
+ , returnS c
+ ]
+
+ -----------------------------
+ -- undersaturated application
+ -----------------------------
+ [ traceRts cfg (jString "h$ap_gen: undersat")
+ -- find PAP entry function corresponding to given_regs count
+ , p |= var "h$paps" .! given_regs
+
+ -- build PAP payload: R1 + tag + given register values
+ , newTag |= ((needed_regs-given_regs) .<<. 8) .|. (needed_args-given_args)
+ , dat |= toJExpr [r1, newTag]
+ , loop 0 (.<. given_regs) \i -> mconcat
+ [ (dat .^ "push") `ApplStat` [stack .! (sp - i - 2)]
+ , postIncrS i
+ ]
+
+ -- remove register values from the stack.
+ , sp |= sp - given_regs - 2
+
+ -- alloc PAP closure, store reference to it in R1.
+ , r1 |= initClosure cfg p dat jCurrentCCS
+
+ -- return to the continuation on the stack
+ , returnStack
+ ]
+ ]
+ ]
+
+-- | Generic fast apply function (h$ap_gen_fast) that can do everything, but less
+-- efficiently than other more specialized functions.
+--
+-- Signature tag in argument. Tag: (regs << 8 | arity)
+--
+-- Regs:
+-- R1 = closure to apply to
+--
+genericFastApply :: StgToJSConfig -> JStat
+genericFastApply s =
+ TxtI "h$ap_gen_fast" ||= jLam \tag -> jVar \c ->
+ [traceRts s (jString "h$ap_gen_fast: " + tag)
+ , c |= closureEntry r1
+ , SwitchStat (entryClosureType c)
+ [ (toJExpr Thunk, traceRts s (jString "h$ap_gen_fast: thunk")
+ <> pushStackApply c tag
+ <> returnS c)
+ , (toJExpr Fun, jVar \farity ->
+ [ farity |= funArity' c
+ , traceRts s (jString "h$ap_gen_fast: fun " + farity)
+ , funCase c tag farity
+ ])
+ , (toJExpr Pap, jVar \parity ->
+ [ parity |= papArity r1
+ , traceRts s (jString "h$ap_gen_fast: pap " + parity)
+ , funCase c tag parity
+ ])
+ , (toJExpr Con, traceRts s (jString "h$ap_gen_fast: con")
+ <> jwhenS (tag .!=. 0)
+ (appS "throw" [jString "h$ap_gen_fast: invalid apply"])
+ <> returnS c)
+ , (toJExpr Blackhole, traceRts s (jString "h$ap_gen_fast: blackhole")
+ <> pushStackApply c tag
+ <> push' s [r1, var "h$return"]
+ <> returnS (app "h$blockOnBlackhole" [r1]))
+ ] $ appS "throw" [jString "h$ap_gen_fast: unexpected closure type: " + entryClosureType c]
+ ]
+
+ where
+ -- thunk: push everything to stack frame, enter thunk first
+ pushStackApply :: JExpr -> JExpr -> JStat
+ pushStackApply _c tag =
+ jVar \ap ->
+ [ pushAllRegs tag
+ , ap |= var "h$apply" .! tag
+ , ifS (ap .===. var "h$ap_gen")
+ ((sp |= sp + 2) <> (stack .! (sp-1) |= tag))
+ (sp |= sp + 1)
+ , stack .! sp |= ap
+ , profStat s pushRestoreCCS
+ ]
+
+ funCase :: JExpr -> JExpr -> JExpr -> JStat
+ funCase c tag arity =
+ jVar \ar myAr myRegs regsStart newTag newAp dat p ->
+ [ ar |= mask8 arity
+ , myAr |= mask8 tag
+ , myRegs |= tag .>>. 8
+ , traceRts s (jString "h$ap_gen_fast: args: " + myAr
+ + jString " regs: " + myRegs)
+ , ifS (myAr .===. ar)
+ -- call the function directly
+ (traceRts s (jString "h$ap_gen_fast: exact") <> returnS c)
+ (ifBlockS (myAr .>. ar)
+ -- push stack frame with remaining args, then call fun
+ [ traceRts s (jString "h$ap_gen_fast: oversat " + sp)
+ , regsStart |= (arity .>>. 8) + 1
+ , sp |= sp + myRegs - regsStart + 1
+ , traceRts s (jString "h$ap_gen_fast: oversat " + sp)
+ , pushArgs regsStart myRegs
+ , newTag |= ((myRegs-( arity.>>.8)).<<.8).|.myAr-ar
+ , newAp |= var "h$apply" .! newTag
+ , ifS (newAp .===. var "h$ap_gen")
+ ((sp |= sp + 2) <> (stack .! (sp - 1) |= newTag))
+ (sp |= sp + 1)
+ , stack .! sp |= newAp
+ , profStat s pushRestoreCCS
+ , returnS c
+ ]
+ -- else
+ [traceRts s (jString "h$ap_gen_fast: undersat: " + myRegs + jString " " + tag)
+ , jwhenS (tag .!=. 0) $ mconcat
+ [ p |= var "h$paps" .! myRegs
+ , dat |= toJExpr [r1, ((arity .>>. 8)-myRegs)*256+ar-myAr]
+ , loop 0 (.<. myRegs)
+ (\i -> (dat .^ "push")
+ `ApplStat` [app "h$getReg" [i+2]] <> postIncrS i)
+ , r1 |= initClosure s p dat jCurrentCCS
+ ]
+ , returnStack
+ ])
+ ]
+
+
+ pushAllRegs :: JExpr -> JStat
+ pushAllRegs tag =
+ jVar \regs ->
+ [ regs |= tag .>>. 8
+ , sp |= sp + regs
+ , SwitchStat regs (map pushReg [65,64..2]) mempty
+ ]
+ where
+ pushReg :: Int -> (JExpr, JStat)
+ pushReg r = (toJExpr (r-1), stack .! (sp - toJExpr (r - 2)) |= jsReg r)
+
+ pushArgs :: JExpr -> JExpr -> JStat
+ pushArgs start end =
+ loop end (.>=.start) (\i -> traceRts s (jString "pushing register: " + i)
+ <> (stack .! (sp + start - i) |= app "h$getReg" [i+1])
+ <> postDecrS i
+ )
+
+-- | Make specialized apply function for the given ApplySpec
+specApply :: StgToJSConfig -> ApplySpec -> JStat
+specApply cfg spec@(ApplySpec conv nargs nvars) =
+ let fun_name = specApplyName spec
+ in case conv of
+ RegsConv -> fastApply cfg fun_name nargs nvars
+ StackConv -> stackApply cfg fun_name nargs nvars
+
+-- | Make specialized apply function with Stack calling convention
+stackApply
+ :: StgToJSConfig
+ -> FastString
+ -> Int
+ -> Int
+ -> JStat
+stackApply s fun_name nargs nvars =
+ -- special case for h$ap_0_0
+ if nargs == 0 && nvars == 0
+ then closure info0 body0
+ else closure info body
+ where
+ info = ClosureInfo (TxtI fun_name) (CIRegs 0 [PtrV]) fun_name (CILayoutUnknown nvars) CIStackFrame mempty
+ info0 = ClosureInfo (TxtI fun_name) (CIRegs 0 [PtrV]) fun_name (CILayoutFixed 0 []) CIStackFrame mempty
+
+ body0 = adjSpN' 1 <> enter s r1
+
+ body = jVar \c ->
+ [ c |= closureEntry r1
+ , traceRts s (toJExpr fun_name
+ + jString " "
+ + (c .^ "n")
+ + jString " sp: " + sp
+ + jString " a: " + (c .^ "a"))
+ , SwitchStat (entryClosureType c)
+ [ (toJExpr Thunk, traceRts s (toJExpr $ fun_name <> ": thunk") <> profStat s pushRestoreCCS <> returnS c)
+ , (toJExpr Fun, traceRts s (toJExpr $ fun_name <> ": fun") <> funCase c)
+ , (toJExpr Pap, traceRts s (toJExpr $ fun_name <> ": pap") <> papCase c)
+ , (toJExpr Blackhole, push' s [r1, var "h$return"] <> returnS (app "h$blockOnBlackhole" [r1]))
+ ] (appS "throw" [toJExpr ("panic: " <> fun_name <> ", unexpected closure type: ") + (entryClosureType c)])
+ ]
+
+ funExact c = popSkip 1 (reverse $ take nvars jsRegsFromR2) <> returnS c
+ stackArgs = map (\x -> stack .! (sp - toJExpr x)) [1..nvars]
+
+ papCase :: JExpr -> JStat
+ papCase c = jVar \expr arity0 arity ->
+ case expr of
+ ValExpr (JVar pap) -> [ arity0 |= papArity r1
+ , arity |= mask8 arity0
+ , traceRts s (toJExpr (fun_name <> ": found pap, arity: ") + arity)
+ , ifS (toJExpr nargs .===. arity)
+ --then
+ (traceRts s (toJExpr (fun_name <> ": exact")) <> funExact c)
+ -- else
+ (ifS (toJExpr nargs .>. arity)
+ (traceRts s (toJExpr (fun_name <> ": oversat")) <> oversatCase c arity0 arity)
+ (traceRts s (toJExpr (fun_name <> ": undersat"))
+ <> mkPap s pap r1 (toJExpr nargs) stackArgs
+ <> (sp |= sp - toJExpr (nvars + 1))
+ <> (r1 |= toJExpr pap)
+ <> returnStack))
+ ]
+ _ -> mempty
+
+
+ funCase :: JExpr -> JStat
+ funCase c = jVar \expr ar0 ar ->
+ case expr of
+ ValExpr (JVar pap) -> [ ar0 |= funArity' c
+ , ar |= mask8 ar0
+ , ifS (toJExpr nargs .===. ar)
+ (traceRts s (toJExpr (fun_name <> ": exact")) <> funExact c)
+ (ifS (toJExpr nargs .>. ar)
+ (traceRts s (toJExpr (fun_name <> ": oversat"))
+ <> oversatCase c ar0 ar)
+ (traceRts s (toJExpr (fun_name <> ": undersat"))
+ <> mkPap s pap (toJExpr R1) (toJExpr nargs) stackArgs
+ <> (sp |= sp - toJExpr (nvars+1))
+ <> (r1 |= toJExpr pap)
+ <> returnStack))
+ ]
+ _ -> mempty
+
+
+ -- oversat: call the function but keep enough on the stack for the next
+ oversatCase :: JExpr -- function
+ -> JExpr -- the arity tag
+ -> JExpr -- real arity (arity & 0xff)
+ -> JStat
+ oversatCase c arity arity0 =
+ jVar \rs newAp ->
+ [ rs |= (arity .>>. 8)
+ , loadRegs rs
+ , sp |= sp - rs
+ , newAp |= (var "h$apply" .! ((toJExpr nargs-arity0).|.((toJExpr nvars-rs).<<.8)))
+ , stack .! sp |= newAp
+ , profStat s pushRestoreCCS
+ , traceRts s (toJExpr (fun_name <> ": new stack frame: ") + (newAp .^ "n"))
+ , returnS c
+ ]
+ where
+ loadRegs rs = SwitchStat rs switchAlts mempty
+ where
+ switchAlts = map (\x -> (toJExpr x, jsReg (x+1) |= stack .! (sp - toJExpr x))) [nvars,nvars-1..1]
+
+-- | Make specialized apply function with Regs calling convention
+--
+-- h$ap_n_r_fast is entered if a function of unknown arity is called, n
+-- arguments are already in r registers
+fastApply :: StgToJSConfig -> FastString -> Int -> Int -> JStat
+fastApply s fun_name nargs nvars = func ||= body0
+ where
+ -- special case for h$ap_0_0_fast
+ body0 = if nargs == 0 && nvars == 0
+ then jLam (enter s r1)
+ else toJExpr (JFunc myFunArgs body)
+
+ func = TxtI fun_name
+
+ myFunArgs = []
+
+ regArgs = take nvars jsRegsFromR2
+
+ mkAp :: Int -> Int -> [JExpr]
+ mkAp n' r' = [ specApplyExpr (ApplySpec StackConv n' r') ]
+
+ body =
+ jVar \c farity arity ->
+ [ c |= closureEntry r1
+ , traceRts s (toJExpr (fun_name <> ": sp ") + sp)
+ , SwitchStat (entryClosureType c)
+ [(toJExpr Fun, traceRts s (toJExpr (fun_name <> ": ")
+ + clName c
+ + jString " (arity: " + (c .^ "a") + jString ")")
+ <> (farity |= funArity' c)
+ <> funCase c farity)
+ ,(toJExpr Pap, traceRts s (toJExpr (fun_name <> ": pap")) <> (arity |= papArity r1) <> funCase c arity)
+ ,(toJExpr Thunk, traceRts s (toJExpr (fun_name <> ": thunk")) <> push' s (reverse regArgs ++ mkAp nargs nvars) <> profStat s pushRestoreCCS <> returnS c)
+ ,(toJExpr Blackhole, traceRts s (toJExpr (fun_name <> ": blackhole")) <> push' s (reverse regArgs ++ mkAp nargs nvars) <> push' s [r1, var "h$return"] <> returnS (app "h$blockOnBlackhole" [r1]))]
+ (appS "throw" [toJExpr (fun_name <> ": unexpected closure type: ") + entryClosureType c])
+ ]
+
+ funCase :: JExpr -> JExpr -> JStat
+ funCase c arity = jVar \arg ar -> case arg of
+ ValExpr (JVar pap) -> [ ar |= mask8 arity
+ , ifS (toJExpr nargs .===. ar)
+ -- then
+ (traceRts s (toJExpr (fun_name <> ": exact")) <> returnS c)
+ -- else
+ (ifS (toJExpr nargs .>. ar)
+ --then
+ (traceRts s (toJExpr (fun_name <> ": oversat")) <> oversatCase c arity)
+ -- else
+ (traceRts s (toJExpr (fun_name <> ": undersat"))
+ <> mkPap s pap r1 (toJExpr nargs) regArgs
+ <> (r1 |= toJExpr pap)
+ <> returnStack))
+ ]
+ _ -> mempty
+
+ oversatCase :: JExpr -> JExpr -> JStat
+ oversatCase c arity =
+ jVar \rs rsRemain ->
+ [ rs |= arity .>>. 8
+ , rsRemain |= toJExpr nvars - rs
+ , traceRts s (toJExpr
+ (fun_name <> " regs oversat ")
+ + rs
+ + jString " remain: "
+ + rsRemain)
+ , saveRegs rs
+ , sp |= sp + rsRemain + 1
+ , stack .! sp |= var "h$apply" .! ((rsRemain.<<.8).|. (toJExpr nargs - mask8 arity))
+ , profStat s pushRestoreCCS
+ , returnS c
+ ]
+ where
+ saveRegs n = SwitchStat n switchAlts mempty
+ where
+ switchAlts = map (\x -> (toJExpr x, stack .! (sp + toJExpr (nvars-x)) |= jsReg (x+2))) [0..nvars-1]
+
+zeroApply :: StgToJSConfig -> JStat
+zeroApply s = mconcat
+ [ TxtI "h$e" ||= jLam (\c -> (r1 |= c) <> enter s c)
+ ]
+
+-- carefully enter a closure that might be a thunk or a function
+
+-- ex may be a local var, but must've been copied to R1 before calling this
+enter :: StgToJSConfig -> JExpr -> JStat
+enter s ex = jVar \c ->
+ [ jwhenS (app "typeof" [ex] .!==. jTyObject) returnStack
+ , c |= closureEntry ex
+ , jwhenS (c .===. var "h$unbox_e") ((r1 |= closureField1 ex) <> returnStack)
+ , SwitchStat (entryClosureType c)
+ [ (toJExpr Con, mempty)
+ , (toJExpr Fun, mempty)
+ , (toJExpr Pap, returnStack)
+ , (toJExpr Blackhole, push' s [var "h$ap_0_0", ex, var "h$return"]
+ <> returnS (app "h$blockOnBlackhole" [ex]))
+ ] (returnS c)
+ ]
+
+updates :: StgToJSConfig -> JStat
+updates s = BlockStat
+ [ closure
+ (ClosureInfo (TxtI "h$upd_frame") (CIRegs 0 [PtrV]) "h$upd_frame" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ $ jVar \updatee waiters ss si sir ->
+ let unbox_closure = Closure
+ { clEntry = var "h$unbox_e"
+ , clField1 = sir
+ , clField2 = null_
+ , clMeta = 0
+ , clCC = Nothing
+ }
+ updateCC updatee = closureCC updatee |= jCurrentCCS
+ in [ updatee |= stack .! (sp - 1)
+ , traceRts s (jString "h$upd_frame updatee alloc: " + updatee .^ "alloc")
+ , -- wake up threads blocked on blackhole
+ waiters |= closureField2 updatee
+ , jwhenS (waiters .!==. null_)
+ (loop 0 (.<. waiters .^ "length")
+ (\i -> appS "h$wakeupThread" [waiters .! i] <> postIncrS i))
+ , -- update selectors
+ jwhenS ((app "typeof" [closureMeta updatee] .===. jTyObject) .&&. (closureMeta updatee .^ "sel"))
+ ((ss |= closureMeta updatee .^ "sel")
+ <> loop 0 (.<. ss .^ "length") \i -> mconcat
+ [ si |= ss .! i
+ , sir |= (closureField2 si) `ApplExpr` [r1]
+ , ifS (app "typeof" [sir] .===. jTyObject)
+ (copyClosure DontCopyCC si sir)
+ (assignClosure si unbox_closure)
+ , postIncrS i
+ ])
+ , -- overwrite the object
+ ifS (app "typeof" [r1] .===. jTyObject)
+ (mconcat [ traceRts s (jString "$upd_frame: boxed: " + ((closureEntry r1) .^ "n"))
+ , copyClosure DontCopyCC updatee r1
+ ])
+ -- the heap object is represented by another type of value
+ -- (e.g. a JS number or string) so the unboxing closure
+ -- will simply return it.
+ (assignClosure updatee (unbox_closure { clField1 = r1 }))
+ , profStat s (updateCC updatee)
+ , adjSpN' 2
+ , traceRts s (jString "h$upd_frame: updating: "
+ + updatee
+ + jString " -> "
+ + r1)
+ , returnStack
+ ]
+
+ , closure
+ (ClosureInfo (TxtI "h$upd_frame_lne") (CIRegs 0 [PtrV]) "h$upd_frame_lne" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ $ jVar \updateePos ->
+ [ updateePos |= stack .! (sp - 1)
+ , (stack .! updateePos |= r1)
+ , adjSpN' 2
+ , traceRts s (jString "h$upd_frame_lne: updating: "
+ + updateePos
+ + jString " -> "
+ + r1)
+ , returnStack
+ ]
+ ]
+
+selectors :: StgToJSConfig -> JStat
+selectors s =
+ mkSel "1" closureField1
+ <> mkSel "2a" closureField2
+ <> mkSel "2b" (closureField1 . closureField2)
+ <> mconcat (map mkSelN [3..16])
+ where
+ mkSelN :: Int -> JStat
+ mkSelN x = mkSel (mkFastString $ show x)
+ (\e -> SelExpr (closureField2 (toJExpr e))
+ (TxtI $ mkFastString ("d" ++ show (x-1))))
+
+
+ mkSel :: FastString -> (JExpr -> JExpr) -> JStat
+ mkSel name sel = mconcat
+ [TxtI createName ||= jLam \r -> mconcat
+ [ traceRts s (toJExpr ("selector create: " <> name <> " for ") + (r .^ "alloc"))
+ , ifS (isThunk r .||. isBlackhole r)
+ (returnS (app "h$mkSelThunk" [r, toJExpr (v entryName), toJExpr (v resName)]))
+ (returnS (sel r))
+ ]
+ , TxtI resName ||= jLam \r -> mconcat
+ [ traceRts s (toJExpr ("selector result: " <> name <> " for ") + (r .^ "alloc"))
+ , returnS (sel r)
+ ]
+ , closure
+ (ClosureInfo (TxtI entryName) (CIRegs 0 [PtrV]) ("select " <> name) (CILayoutFixed 1 [PtrV]) CIThunk mempty)
+ (jVar \tgt ->
+ [ tgt |= closureField1 r1
+ , traceRts s (toJExpr ("selector entry: " <> name <> " for ") + (tgt .^ "alloc"))
+ , ifS (isThunk tgt .||. isBlackhole tgt)
+ (preIncrS sp
+ <> (stack .! sp |= var frameName)
+ <> returnS (app "h$e" [tgt]))
+ (returnS (app "h$e" [sel tgt]))
+ ])
+ , closure
+ (ClosureInfo (TxtI frameName) (CIRegs 0 [PtrV]) ("select " <> name <> " frame") (CILayoutFixed 0 []) CIStackFrame mempty)
+ $ mconcat [ traceRts s (toJExpr ("selector frame: " <> name))
+ , postDecrS sp
+ , returnS (app "h$e" [sel r1])
+ ]
+ ]
+
+ where
+ v x = JVar (TxtI x)
+ n ext = "h$c_sel_" <> name <> ext
+ createName = n ""
+ resName = n "_res"
+ entryName = n "_e"
+ frameName = n "_frame_e"
+
+
+-- arity is the remaining arity after our supplied arguments are applied
+mkPap :: StgToJSConfig
+ -> Ident -- ^ id of the pap object
+ -> JExpr -- ^ the function that's called (can be a second pap)
+ -> JExpr -- ^ number of arguments in pap
+ -> [JExpr] -- ^ values for the supplied arguments
+ -> JStat
+mkPap s tgt fun n values =
+ traceRts s (toJExpr $ "making pap with: " ++ show (length values) ++ " items")
+ `mappend`
+ allocDynamic s True tgt (toJExpr entry) (fun:papAr:map toJExpr values')
+ (if csProf s then Just jCurrentCCS else Nothing)
+ where
+ papAr = funOrPapArity fun Nothing - toJExpr (length values * 256) - n
+
+ values' | GHC.Prelude.null values = [null_]
+ | otherwise = values
+ entry | length values > numSpecPap = TxtI "h$pap_gen"
+ | otherwise = specPapIdents ! length values
+
+-- | Number of specialized PAPs (pre-generated for a given number of args)
+numSpecPap :: Int
+numSpecPap = 6
+
+-- specialized (faster) pap generated for [0..numSpecPap]
+-- others use h$pap_gen
+specPap :: [Int]
+specPap = [0..numSpecPap]
+
+-- | Cache of specialized PAP idents
+specPapIdents :: Array Int Ident
+specPapIdents = listArray (0,numSpecPap) $ map (TxtI . mkFastString . ("h$pap_"++) . show) specPap
+
+pap :: StgToJSConfig
+ -> Int
+ -> JStat
+pap s r = closure (ClosureInfo funcIdent CIRegsUnknown funcName (CILayoutUnknown (r+2)) CIPap mempty) body
+ where
+ funcIdent = TxtI funcName
+ funcName = mkFastString ("h$pap_" ++ show r)
+
+ body = jVar \c d f extra ->
+ [ c |= closureField1 r1
+ , d |= closureField2 r1
+ , f |= closureEntry c
+ , assertRts s (isFun' f .||. isPap' f) (funcName <> ": expected function or pap")
+ , profStat s (enterCostCentreFun currentCCS)
+ , extra |= (funOrPapArity c (Just f) .>>. 8) - toJExpr r
+ , traceRts s (toJExpr (funcName <> ": pap extra args moving: ") + extra)
+ , moveBy extra
+ , loadOwnArgs d
+ , r1 |= c
+ , returnS f
+ ]
+ moveBy extra = SwitchStat extra
+ (reverse $ map moveCase [1..maxReg-r-1]) mempty
+ moveCase m = (toJExpr m, jsReg (m+r+1) |= jsReg (m+1))
+ loadOwnArgs d = mconcat $ map (\r ->
+ jsReg (r+1) |= dField d (r+2)) [1..r]
+ dField d n = SelExpr d (TxtI . mkFastString $ ('d':show (n-1)))
+
+-- Construct a generic PAP
+papGen :: StgToJSConfig -> JStat
+papGen cfg =
+ closure (ClosureInfo funcIdent CIRegsUnknown funcName CILayoutVariable CIPap mempty)
+ (jVar \c f d pr or r ->
+ [ c |= closureField1 r1
+ , d |= closureField2 r1
+ , f |= closureEntry c
+ , pr |= funOrPapArity c (Just f) .>>. 8
+ , or |= papArity r1 .>>. 8
+ , r |= pr - or
+ , assertRts cfg
+ (isFun' f .||. isPap' f)
+ (jString "h$pap_gen: expected function or pap")
+ , profStat cfg (enterCostCentreFun currentCCS)
+ , traceRts cfg (jString "h$pap_gen: generic pap extra args moving: " + or)
+ , appS "h$moveRegs2" [or, r]
+ , loadOwnArgs d r
+ , r1 |= c
+ , returnS f
+ ])
+
+
+ where
+ funcIdent = TxtI funcName
+ funcName = "h$pap_gen"
+ loadOwnArgs d r =
+ let prop n = d .^ ("d" <> mkFastString (show $ n+1))
+ loadOwnArg n = (toJExpr n, jsReg (n+1) |= prop n)
+ in SwitchStat r (map loadOwnArg [127,126..1]) mempty
+
+-- general utilities
+-- move the first n registers, starting at R2, m places up (do not use with negative m)
+moveRegs2 :: JStat
+moveRegs2 = TxtI "h$moveRegs2" ||= jLam moveSwitch
+ where
+ moveSwitch n m = SwitchStat ((n .<<. 8) .|. m) switchCases (defaultCase n m)
+ -- fast cases
+ switchCases = [switchCase n m | n <- [1..5], m <- [1..4]]
+ switchCase :: Int -> Int -> (JExpr, JStat)
+ switchCase n m = (toJExpr $
+ (n `Bits.shiftL` 8) Bits..|. m
+ , mconcat (map (`moveRegFast` m) [n+1,n..2])
+ <> BreakStat Nothing {-[j| break; |]-})
+ moveRegFast n m = jsReg (n+m) |= jsReg n
+ -- fallback
+ defaultCase n m =
+ loop n (.>.0) (\i -> appS "h$setReg" [i+1+m, app "h$getReg" [i+1]] `mappend` postDecrS i)
+
+
+-- Initalize a variable sized object from an array of values
+initClosure :: StgToJSConfig -> JExpr -> JExpr -> JExpr -> JExpr
+initClosure cfg entry values ccs = app "h$init_closure"
+ [ newClosure $ Closure
+ { clEntry = entry
+ , clField1 = null_
+ , clField2 = null_
+ , clMeta = 0
+ , clCC = if csProf cfg then Just ccs else Nothing
+ }
+ , values
+ ]
+
+-- | Return an expression for every field of the given Id
+getIdFields :: Id -> G [TypedExpr]
+getIdFields i = assocIdExprs i <$> varsForId i
+
+-- | Store fields of Id into the given target expressions
+storeIdFields :: Id -> [TypedExpr] -> G JStat
+storeIdFields i dst = do
+ fields <- getIdFields i
+ pure (assignCoerce1 dst fields)
diff --git a/compiler/GHC/StgToJS/Arg.hs b/compiler/GHC/StgToJS/Arg.hs
new file mode 100644
index 0000000000..854bf7cc17
--- /dev/null
+++ b/compiler/GHC/StgToJS/Arg.hs
@@ -0,0 +1,285 @@
+{-# LANGUAGE LambdaCase #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Args
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Code generation of application arguments
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Arg
+ ( genArg
+ , genIdArg
+ , genIdArgI
+ , genIdStackArgI
+ , allocConStatic
+ , allocUnboxedConStatic
+ , allocateStaticList
+ , jsStaticArg
+ , jsStaticArgs
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.DataCon
+import GHC.StgToJS.Types
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Literal
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Ids
+
+import GHC.Builtin.Types
+import GHC.Stg.Syntax
+import GHC.Core.DataCon
+
+import GHC.Types.CostCentre
+import GHC.Types.Unique.FM
+import GHC.Types.Id
+
+import GHC.Utils.Misc
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+import qualified Control.Monad.Trans.State.Strict as State
+
+{-
+Note [ Unboxable Literals Optimization ]
+~~~~~~~~~~~~~~~~~~
+
+Boxable types in the JS backend are represented as heap objects. See Note
+[StgToJS design] in GHC.StgToJS.hs for more details. Some types, such as Int8
+do not benefit from not being wrapped in an object in the JS runtime. This optimization
+detects such types and changes the code generator to generate a more efficient
+representation. The change is minor and saves one level on indirection. Instead
+of generating a wrapper object with a field for the value's payload, such as:
+
+// a JS object for an Int8
+var anInt8 = { d1 = <Int8# payload>
+ , f : entry function which would scrutinize the payload
+ }
+
+we instead generate:
+
+// notice, no wrapper object. This representation is essentially an Int8# in the JS backend
+var anInt8 = <Int8# payload>
+
+This optimization fires when the follow invariants hold:
+ 1. The value in question has a Type which has a single data constructor
+ 2. The data constructor holds a single field that is monomorphic
+ 3. The value in question is distinguishable from a THUNK using the JavaScript typeof operator.
+
+From the haskell perspective this means that:
+ 1. An Int8# is always a JavaScript 'number', never a JavaScript object.
+ 2. An Int8 is either a JavaScript 'number' _or_ a JavaScript object depending on
+ its use case and this optimization.
+
+How is this sound?
+~~~~~~~~~~~~~~~~~~
+
+Normally this optimization would violate the guarantees of call-by-need, however
+we are able to statically detect whether the type in question will be a THUNK or
+not during code gen because the JS backend is consuming STG and we can check
+during runtime with the typeof operator. Similarly we can check at runtime using
+JavaScript's introspection operator `typeof`. Thus, when we know the value in
+question will not be a THUNK we can safely elide the wrapping object, which
+unboxes the value in the JS runtime. For example, an Int8 contains an Int8#
+which has the JavaScript type 'number'. A THUNK of type Int8 would have a
+JavaScript type 'object', so using 'typeof' allows us to check if we have
+something that is definitely evaluated (i.e., a 'number') or something else. If
+it is an 'object' then we may need to enter it to begin its evaluation. Consider
+a type which has a 'ThreadId#' field; such as type would not be subject to this
+optimization because it has to be represented as a JavaScript 'object' and thus
+cannot be unboxed in this way. Another (edge) case is Int64#. Int64# is
+similarly not unboxable in this way because Int64# does not fit in one
+JavaScript variable and thus requires an 'object' for its representation in the
+JavaScript runtime.
+
+-}
+
+-- | Generate JS code for static arguments
+genStaticArg :: HasDebugCallStack => StgArg -> G [StaticArg]
+genStaticArg a = case a of
+ StgLitArg l -> map StaticLitArg <$> genStaticLit l
+ StgVarArg i -> do
+ unFloat <- State.gets gsUnfloated
+ case lookupUFM unFloat i of
+ Nothing -> reg
+ Just expr -> unfloated expr
+ where
+ r = uTypeVt . stgArgType $ a
+ reg
+ | isVoid r =
+ return []
+ | i == trueDataConId =
+ return [StaticLitArg (BoolLit True)]
+ | i == falseDataConId =
+ return [StaticLitArg (BoolLit False)]
+ | isMultiVar r =
+ map (\(TxtI t) -> StaticObjArg t) <$> mapM (identForIdN i) [1..varSize r] -- this seems wrong, not an obj?
+ | otherwise = (\(TxtI it) -> [StaticObjArg it]) <$> identForId i
+
+ unfloated :: CgStgExpr -> G [StaticArg]
+ unfloated (StgLit l) = map StaticLitArg <$> genStaticLit l
+ unfloated (StgConApp dc _n args _)
+ | isBoolDataCon dc || isUnboxableCon dc =
+ (:[]) . allocUnboxedConStatic dc . concat <$> mapM genStaticArg args -- fixme what is allocunboxedcon?
+ | null args = (\(TxtI t) -> [StaticObjArg t]) <$> identForId (dataConWorkId dc)
+ | otherwise = do
+ as <- concat <$> mapM genStaticArg args
+ (TxtI e) <- identForDataConWorker dc
+ return [StaticConArg e as]
+ unfloated x = pprPanic "genArg: unexpected unfloated expression" (pprStgExpr panicStgPprOpts x)
+
+-- | Generate JS code for an StgArg
+genArg :: HasDebugCallStack => StgArg -> G [JExpr]
+genArg a = case a of
+ StgLitArg l -> genLit l
+ StgVarArg i -> do
+ unFloat <- State.gets gsUnfloated
+ case lookupUFM unFloat i of
+ Just expr -> unfloated expr
+ Nothing
+ | isVoid r -> return []
+ | i == trueDataConId -> return [true_]
+ | i == falseDataConId -> return [false_]
+ | isMultiVar r -> mapM (varForIdN i) [1..varSize r]
+ | otherwise -> (:[]) <$> varForId i
+
+ where
+ -- if our argument is a joinid, it can be an unboxed tuple
+ r :: HasDebugCallStack => VarType
+ r = uTypeVt . stgArgType $ a
+
+ unfloated :: HasDebugCallStack => CgStgExpr -> G [JExpr]
+ unfloated = \case
+ StgLit l -> genLit l
+ StgConApp dc _n args _
+ | isBoolDataCon dc || isUnboxableCon dc
+ -> (:[]) . allocUnboxedCon dc . concat <$> mapM genArg args
+ | null args -> (:[]) <$> varForId (dataConWorkId dc)
+ | otherwise -> do
+ as <- concat <$> mapM genArg args
+ e <- varForDataConWorker dc
+ inl_alloc <- csInlineAlloc <$> getSettings
+ return [allocDynamicE inl_alloc e as Nothing]
+ x -> pprPanic "genArg: unexpected unfloated expression" (pprStgExpr panicStgPprOpts x)
+
+-- | Generate a Var as JExpr
+genIdArg :: HasDebugCallStack => Id -> G [JExpr]
+genIdArg i = genArg (StgVarArg i)
+
+-- | Generate an Id as an Ident
+genIdArgI :: HasDebugCallStack => Id -> G [Ident]
+genIdArgI i
+ | isVoid r = return []
+ | isMultiVar r = mapM (identForIdN i) [1..varSize r]
+ | otherwise = (:[]) <$> identForId i
+ where
+ r = uTypeVt . idType $ i
+
+-- | Generate IDs for stack arguments. See 'StgToJS.Expr.loadRetArgs' for use case
+genIdStackArgI :: HasDebugCallStack => Id -> G [(Ident,StackSlot)]
+genIdStackArgI i = zipWith f [1..] <$> genIdArgI i
+ where
+ f :: Int -> Ident -> (Ident,StackSlot)
+ f n ident = (ident, SlotId i n)
+
+-- | Allocate Static Constructors
+allocConStatic :: HasDebugCallStack => Ident -> CostCentreStack -> DataCon -> [StgArg] -> G ()
+allocConStatic (TxtI to) cc con args = do
+ as <- mapM genStaticArg args
+ cc' <- costCentreStackLbl cc
+ allocConStatic' cc' (concat as)
+ where
+ -- see Note [ Unboxable Literals Optimization ] for the purpose of these
+ -- checks
+ allocConStatic' :: HasDebugCallStack => Maybe Ident -> [StaticArg] -> G ()
+ allocConStatic' cc' []
+ | isBoolDataCon con && dataConTag con == 1 =
+ emitStatic to (StaticUnboxed $ StaticUnboxedBool False) cc'
+ | isBoolDataCon con && dataConTag con == 2 =
+ emitStatic to (StaticUnboxed $ StaticUnboxedBool True) cc'
+ | otherwise = do
+ (TxtI e) <- identForDataConWorker con
+ emitStatic to (StaticData e []) cc'
+ allocConStatic' cc' [x]
+ | isUnboxableCon con =
+ case x of
+ StaticLitArg (IntLit i) ->
+ emitStatic to (StaticUnboxed $ StaticUnboxedInt i) cc'
+ StaticLitArg (BoolLit b) ->
+ emitStatic to (StaticUnboxed $ StaticUnboxedBool b) cc'
+ StaticLitArg (DoubleLit d) ->
+ emitStatic to (StaticUnboxed $ StaticUnboxedDouble d) cc'
+ _ ->
+ pprPanic "allocConStatic: invalid unboxed literal" (ppr x)
+ allocConStatic' cc' xs =
+ if con == consDataCon
+ then case args of
+ (a0:a1:_) -> flip (emitStatic to) cc' =<< allocateStaticList [a0] a1
+ _ -> panic "allocConStatic: invalid args for consDataCon"
+ else do
+ (TxtI e) <- identForDataConWorker con
+ emitStatic to (StaticData e xs) cc'
+
+-- | Allocate unboxed constructors
+allocUnboxedConStatic :: DataCon -> [StaticArg] -> StaticArg
+allocUnboxedConStatic con = \case
+ []
+ | isBoolDataCon con && dataConTag con == 1
+ -> StaticLitArg (BoolLit False)
+ | isBoolDataCon con && dataConTag con == 2
+ -> StaticLitArg (BoolLit True)
+ [a@(StaticLitArg (IntLit _i))] -> a
+ [a@(StaticLitArg (DoubleLit _d))] -> a
+ _ -> pprPanic "allocUnboxedConStatic: not an unboxed constructor" (ppr con)
+
+
+-- | Allocate Static list
+allocateStaticList :: [StgArg] -> StgArg -> G StaticVal
+allocateStaticList xs a@(StgVarArg i)
+ | isDataConId_maybe i == Just nilDataCon = listAlloc xs Nothing
+ | otherwise = do
+ unFloat <- State.gets gsUnfloated
+ case lookupUFM unFloat i of
+ Just (StgConApp dc _n [h,t] _)
+ | dc == consDataCon -> allocateStaticList (h:xs) t
+ _ -> listAlloc xs (Just a)
+ where
+ listAlloc :: [StgArg] -> Maybe StgArg -> G StaticVal
+ listAlloc xs Nothing = do
+ as <- concat . reverse <$> mapM genStaticArg xs
+ return (StaticList as Nothing)
+ listAlloc xs (Just r) = do
+ as <- concat . reverse <$> mapM genStaticArg xs
+ r' <- genStaticArg r
+ case r' of
+ [StaticObjArg ri] -> return (StaticList as (Just ri))
+ _ ->
+ pprPanic "allocateStaticList: invalid argument (tail)" (ppr (xs, r))
+allocateStaticList _ _ = panic "allocateStaticList: unexpected literal in list"
+
+-- | Generate JS code corresponding to a static arg
+jsStaticArg :: StaticArg -> JExpr
+jsStaticArg = \case
+ StaticLitArg l -> toJExpr l
+ StaticObjArg t -> ValExpr (JVar (TxtI t))
+ StaticConArg c args ->
+ allocDynamicE False (ValExpr . JVar . TxtI $ c) (map jsStaticArg args) Nothing
+
+-- | Generate JS code corresponding to a list of static args
+jsStaticArgs :: [StaticArg] -> JExpr
+jsStaticArgs = ValExpr . JList . map jsStaticArg
+
diff --git a/compiler/GHC/StgToJS/Closure.hs b/compiler/GHC/StgToJS/Closure.hs
new file mode 100644
index 0000000000..7c758ede95
--- /dev/null
+++ b/compiler/GHC/StgToJS/Closure.hs
@@ -0,0 +1,156 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module GHC.StgToJS.Closure
+ ( closureInfoStat
+ , closure
+ , conClosure
+ , Closure (..)
+ , newClosure
+ , assignClosure
+ , CopyCC (..)
+ , copyClosure
+ )
+where
+
+import GHC.Prelude
+import GHC.Data.FastString
+
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Types
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Regs (stack,sp)
+
+import GHC.JS.Make
+import GHC.JS.Syntax
+
+import Data.Monoid
+import qualified Data.Bits as Bits
+
+closureInfoStat :: Bool -> ClosureInfo -> JStat
+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
+ -> JStat
+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 -> toTypeList (replicate size ObjV)
+ CILayoutFixed _ fs -> toTypeList fs
+
+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
+ -> JStat
+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 $ map varSize types
+ in skip + (nregs `Bits.shiftL` 8)
+
+closure :: ClosureInfo -- ^ object being info'd see @ciVar@ in @ClosureInfo@
+ -> JStat -- ^ rhs
+ -> JStat
+closure ci body = (ciVar ci ||= jLam body) `mappend` closureInfoStat False ci
+
+conClosure :: Ident -> FastString -> CILayout -> Int -> JStat
+conClosure symbol name layout constr =
+ closure (ClosureInfo symbol (CIRegs 0 [PtrV]) name layout (CICon constr) mempty)
+ (returnS (stack .! sp))
+
+-- | Used to pass arguments to newClosure with some safety
+data Closure = Closure
+ { clEntry :: JExpr
+ , clField1 :: JExpr
+ , clField2 :: JExpr
+ , clMeta :: JExpr
+ , clCC :: Maybe JExpr
+ }
+
+newClosure :: Closure -> JExpr
+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 :: JExpr -> Closure -> JStat
+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 -> JExpr -> JExpr -> JStat
+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
diff --git a/compiler/GHC/StgToJS/CodeGen.hs b/compiler/GHC/StgToJS/CodeGen.hs
new file mode 100644
index 0000000000..7703398aea
--- /dev/null
+++ b/compiler/GHC/StgToJS/CodeGen.hs
@@ -0,0 +1,367 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE LambdaCase #-}
+
+-- | JavaScript code generator
+module GHC.StgToJS.CodeGen
+ ( stgToJS
+ )
+where
+
+import GHC.Prelude
+
+import GHC.Driver.Flags (DumpFlag (Opt_D_dump_js))
+
+import GHC.JS.Ppr
+import GHC.JS.Syntax
+import GHC.JS.Make
+import GHC.JS.Transform
+
+import GHC.StgToJS.Arg
+import GHC.StgToJS.Sinker
+import GHC.StgToJS.Types
+import qualified GHC.StgToJS.Object as Object
+import GHC.StgToJS.StgUtils
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Deps
+import GHC.StgToJS.Expr
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Regs
+import GHC.StgToJS.StaticPtr
+import GHC.StgToJS.Symbols
+import GHC.StgToJS.Stack
+import GHC.StgToJS.Ids
+
+import GHC.Stg.Syntax
+import GHC.Core.DataCon
+import GHC.Core.TyCo.Rep (scaledThing)
+
+import GHC.Unit.Module
+import GHC.Linker.Types (SptEntry (..))
+
+import GHC.Types.CostCentre
+import GHC.Types.ForeignStubs (ForeignStubs (..), getCHeader, getCStub)
+import GHC.Types.RepType
+import GHC.Types.Id
+import GHC.Types.Unique
+
+import GHC.Data.FastString
+import GHC.Utils.Encoding
+import GHC.Utils.Logger
+import GHC.Utils.Panic
+import GHC.Utils.Misc
+import GHC.Utils.Binary
+import qualified Control.Monad.Trans.State.Strict as State
+import GHC.Utils.Outputable hiding ((<>))
+
+import qualified Data.Set as S
+import Data.Monoid
+import Control.Monad
+import System.Directory
+import System.FilePath
+
+-- | Code generator for JavaScript
+stgToJS
+ :: Logger
+ -> StgToJSConfig
+ -> [CgStgTopBinding]
+ -> Module
+ -> [SptEntry]
+ -> ForeignStubs
+ -> CollectedCCs
+ -> FilePath -- ^ Output file name
+ -> IO ()
+stgToJS logger config stg_binds0 this_mod spt_entries foreign_stubs cccs output_fn = do
+
+ let (unfloated_binds, stg_binds) = sinkPgm this_mod stg_binds0
+ -- TODO: avoid top level lifting in core-2-core when the JS backend is
+ -- enabled instead of undoing it here
+
+ -- TODO: add dump pass for optimized STG ast for JS
+
+ (deps,lus) <- runG config this_mod unfloated_binds $ do
+ ifProfilingM $ initCostCentres cccs
+ lus <- genUnits this_mod stg_binds spt_entries foreign_stubs
+ deps <- genDependencyData this_mod lus
+ pure (deps,lus)
+
+ -- Doc to dump when -ddump-js is enabled
+ when (logHasDumpFlag logger Opt_D_dump_js) $ do
+ putDumpFileMaybe logger Opt_D_dump_js "JavaScript code" FormatJS
+ $ vcat (fmap (docToSDoc . jsToDoc . oiStat . luObjUnit) lus)
+
+ -- Write the object file
+ bh <- openBinMem (4 * 1024 * 1000) -- a bit less than 4kB
+ Object.putObject bh (moduleName this_mod) deps (map luObjUnit lus)
+
+ createDirectoryIfMissing True (takeDirectory output_fn)
+ writeBinMem bh output_fn
+
+
+
+-- | Generate the ingredients for the linkable units for this module
+genUnits :: HasDebugCallStack
+ => Module
+ -> [CgStgTopBinding]
+ -> [SptEntry]
+ -> ForeignStubs
+ -> G [LinkableUnit] -- ^ the linkable units
+genUnits m ss spt_entries foreign_stubs = do
+ gbl <- generateGlobalBlock
+ exports <- generateExportsBlock
+ others <- go 2 ss
+ pure (gbl:exports:others)
+ where
+ go :: HasDebugCallStack
+ => Int -- the block we're generating (block 0 is the global unit for the module)
+ -> [CgStgTopBinding]
+ -> G [LinkableUnit]
+ go !n = \case
+ [] -> pure []
+ (x:xs) -> do
+ mlu <- generateBlock x n
+ lus <- go (n+1) xs
+ return (maybe lus (:lus) mlu)
+
+ -- Generate the global unit that all other blocks in the module depend on
+ -- used for cost centres and static initializers
+ -- the global unit has no dependencies, exports the moduleGlobalSymbol
+ generateGlobalBlock :: HasDebugCallStack => G LinkableUnit
+ generateGlobalBlock = do
+ glbl <- State.gets gsGlobal
+ staticInit <-
+ initStaticPtrs spt_entries
+ let stat = ( -- O.optimize .
+ jsSaturate (Just $ modulePrefix m 1)
+ $ mconcat (reverse glbl) <> staticInit)
+ let syms = [moduleGlobalSymbol m]
+ let oi = ObjUnit
+ { oiSymbols = syms
+ , oiClInfo = []
+ , oiStatic = []
+ , oiStat = stat
+ , oiRaw = mempty
+ , oiFExports = []
+ , oiFImports = []
+ }
+ let lu = LinkableUnit
+ { luObjUnit = oi
+ , luIdExports = []
+ , luOtherExports = syms
+ , luIdDeps = []
+ , luPseudoIdDeps = []
+ , luOtherDeps = []
+ , luRequired = False
+ , luForeignRefs = []
+ }
+ pure lu
+
+ generateExportsBlock :: HasDebugCallStack => G LinkableUnit
+ generateExportsBlock = do
+ let (f_hdr, f_c) = case foreign_stubs of
+ NoStubs -> (empty, empty)
+ ForeignStubs hdr c -> (getCHeader hdr, getCStub c)
+ unique_deps = map mkUniqueDep (lines $ renderWithContext defaultSDocContext f_hdr)
+ mkUniqueDep (tag:xs) = mkUnique tag (read xs)
+ mkUniqueDep [] = panic "mkUniqueDep"
+
+ let syms = [moduleExportsSymbol m]
+ let raw = utf8EncodeByteString $ renderWithContext defaultSDocContext f_c
+ let oi = ObjUnit
+ { oiSymbols = syms
+ , oiClInfo = []
+ , oiStatic = []
+ , oiStat = mempty
+ , oiRaw = raw
+ , oiFExports = []
+ , oiFImports = []
+ }
+ let lu = LinkableUnit
+ { luObjUnit = oi
+ , luIdExports = []
+ , luOtherExports = syms
+ , luIdDeps = []
+ , luPseudoIdDeps = unique_deps
+ , luOtherDeps = []
+ , luRequired = True
+ , luForeignRefs = []
+ }
+ pure lu
+
+ -- Generate the linkable unit for one binding or group of
+ -- mutually recursive bindings
+ generateBlock :: HasDebugCallStack
+ => CgStgTopBinding
+ -> Int
+ -> G (Maybe LinkableUnit)
+ generateBlock top_bind n = case top_bind of
+ StgTopStringLit bnd str -> do
+ bids <- identsForId bnd
+ case bids of
+ [(TxtI b1t),(TxtI b2t)] -> do
+ -- [e1,e2] <- genLit (MachStr str)
+ emitStatic b1t (StaticUnboxed (StaticUnboxedString str)) Nothing
+ emitStatic b2t (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing
+ _extraTl <- State.gets (ggsToplevelStats . gsGroup)
+ si <- State.gets (ggsStatic . gsGroup)
+ let body = mempty -- mconcat (reverse extraTl) <> b1 ||= e1 <> b2 ||= e2
+ let stat = jsSaturate (Just $ modulePrefix m n) body
+ let ids = [bnd]
+ syms <- (\(TxtI i) -> [i]) <$> identForId bnd
+ let oi = ObjUnit
+ { oiSymbols = syms
+ , oiClInfo = []
+ , oiStatic = si
+ , oiStat = stat
+ , oiRaw = ""
+ , oiFExports = []
+ , oiFImports = []
+ }
+ let lu = LinkableUnit
+ { luObjUnit = oi
+ , luIdExports = ids
+ , luOtherExports = []
+ , luIdDeps = []
+ , luPseudoIdDeps = []
+ , luOtherDeps = []
+ , luRequired = False
+ , luForeignRefs = []
+ }
+ pure (Just lu)
+ _ -> panic "generateBlock: invalid size"
+
+ StgTopLifted decl -> do
+ tl <- genToplevel decl
+ extraTl <- State.gets (ggsToplevelStats . gsGroup)
+ ci <- State.gets (ggsClosureInfo . gsGroup)
+ si <- State.gets (ggsStatic . gsGroup)
+ unf <- State.gets gsUnfloated
+ extraDeps <- State.gets (ggsExtraDeps . gsGroup)
+ fRefs <- State.gets (ggsForeignRefs . gsGroup)
+ resetGroup
+ let allDeps = collectIds unf decl
+ topDeps = collectTopIds decl
+ required = hasExport decl
+ stat = -- Opt.optimize .
+ jsSaturate (Just $ modulePrefix m n)
+ $ mconcat (reverse extraTl) <> tl
+ syms <- mapM (fmap (\(TxtI i) -> i) . identForId) topDeps
+ let oi = ObjUnit
+ { oiSymbols = syms
+ , oiClInfo = ci
+ , oiStatic = si
+ , oiStat = stat
+ , oiRaw = ""
+ , oiFExports = []
+ , oiFImports = fRefs
+ }
+ let lu = LinkableUnit
+ { luObjUnit = oi
+ , luIdExports = topDeps
+ , luOtherExports = []
+ , luIdDeps = allDeps
+ , luPseudoIdDeps = []
+ , luOtherDeps = S.toList extraDeps
+ , luRequired = required
+ , luForeignRefs = fRefs
+ }
+ pure $! seqList topDeps `seq` seqList allDeps `seq` Just lu
+
+-- | variable prefix for the nth block in module
+modulePrefix :: Module -> Int -> FastString
+modulePrefix m n =
+ let encMod = zEncodeString . moduleNameString . moduleName $ m
+ in mkFastString $ "h$" ++ encMod ++ "_id_" ++ show n
+
+genToplevel :: CgStgBinding -> G JStat
+genToplevel (StgNonRec bndr rhs) = genToplevelDecl bndr rhs
+genToplevel (StgRec bs) =
+ mconcat <$> mapM (\(bndr, rhs) -> genToplevelDecl bndr rhs) bs
+
+genToplevelDecl :: Id -> CgStgRhs -> G JStat
+genToplevelDecl i rhs = do
+ s1 <- resetSlots (genToplevelConEntry i rhs)
+ s2 <- resetSlots (genToplevelRhs i rhs)
+ return (s1 <> s2)
+
+genToplevelConEntry :: Id -> CgStgRhs -> G JStat
+genToplevelConEntry i rhs = case rhs of
+ StgRhsCon _cc con _mu _ts _args
+ | isDataConWorkId i
+ -> genSetConInfo i con (stgRhsLive rhs) -- NoSRT
+ StgRhsClosure _ _cc _upd_flag _args _body
+ | Just dc <- isDataConWorkId_maybe i
+ -> genSetConInfo i dc (stgRhsLive rhs) -- srt
+ _ -> pure mempty
+
+genSetConInfo :: HasDebugCallStack => Id -> DataCon -> LiveVars -> G JStat
+genSetConInfo i d l {- srt -} = do
+ ei <- identForDataConEntryId i
+ sr <- genStaticRefs l
+ emitClosureInfo $ ClosureInfo ei
+ (CIRegs 0 [PtrV])
+ (mkFastString $ renderWithContext defaultSDocContext (ppr d))
+ (fixedLayout $ map uTypeVt fields)
+ (CICon $ dataConTag d)
+ sr
+ return (ei ||= mkDataEntry)
+ where
+ -- dataConRepArgTys sometimes returns unboxed tuples. is that a bug?
+ fields = concatMap (map primRepToType . typePrimRep . unwrapType . scaledThing)
+ (dataConRepArgTys d)
+ -- concatMap (map slotTyToType . repTypeSlots . repType) (dataConRepArgTys d)
+
+mkDataEntry :: JExpr
+mkDataEntry = ValExpr $ JFunc [] returnStack
+
+genToplevelRhs :: Id -> CgStgRhs -> G JStat
+-- general cases:
+genToplevelRhs i rhs = case rhs of
+ StgRhsCon cc con _mu _tys args -> do
+ ii <- identForId i
+ allocConStatic ii cc con args
+ return mempty
+ StgRhsClosure _ext cc _upd_flag {- srt -} args body -> do
+ {-
+ algorithm:
+ - collect all Id refs that are in the global id cache
+ - count usage in body for each ref
+ - order by increasing use
+ - prepend loading lives var to body: body can stay the same
+ -}
+ eid@(TxtI eidt) <- identForEntryId i
+ (TxtI idt) <- identForId i
+ body <- genBody (initExprCtx i) i R2 args body
+ global_occs <- globalOccs (jsSaturate (Just "ghcjs_tmp_sat_") body)
+ let lidents = map global_ident global_occs
+ let lids = map global_id global_occs
+ let lidents' = map identFS lidents
+ CIStaticRefs sr0 <- genStaticRefsRhs rhs
+ let sri = filter (`notElem` lidents') sr0
+ sr = CIStaticRefs sri
+ et <- genEntryType args
+ ll <- loadLiveFun lids
+ (static, regs, upd) <-
+ if et == CIThunk
+ then do
+ r <- updateThunk
+ pure (StaticThunk (Just (eidt, map StaticObjArg lidents')), CIRegs 0 [PtrV],r)
+ else return (StaticFun eidt (map StaticObjArg lidents'),
+ (if null lidents then CIRegs 1 (concatMap idVt args)
+ else CIRegs 0 (PtrV : concatMap idVt args))
+ , mempty)
+ setcc <- ifProfiling $
+ if et == CIThunk
+ then enterCostCentreThunk
+ else enterCostCentreFun cc
+ emitClosureInfo (ClosureInfo eid
+ regs
+ idt
+ (fixedLayout $ map (uTypeVt . idType) lids)
+ et
+ sr)
+ ccId <- costCentreStackLbl cc
+ emitStatic idt static ccId
+ return $ (eid ||= toJExpr (JFunc [] (ll <> upd <> setcc <> body)))
diff --git a/compiler/GHC/StgToJS/CoreUtils.hs b/compiler/GHC/StgToJS/CoreUtils.hs
new file mode 100644
index 0000000000..0fdf7a5ed8
--- /dev/null
+++ b/compiler/GHC/StgToJS/CoreUtils.hs
@@ -0,0 +1,282 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | Core utils
+module GHC.StgToJS.CoreUtils where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+
+import GHC.StgToJS.Types
+
+import GHC.Stg.Syntax
+
+import GHC.Tc.Utils.TcType
+
+import GHC.Builtin.Types
+import GHC.Builtin.Types.Prim
+
+import GHC.Core.DataCon
+import GHC.Core.TyCo.Rep
+import GHC.Core.TyCon
+import GHC.Core.Type
+
+import GHC.Types.RepType
+import GHC.Types.Var
+import GHC.Types.Id
+
+import GHC.Utils.Misc
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+
+import qualified Data.Bits as Bits
+
+-- | can we unbox C x to x, only if x is represented as a Number
+isUnboxableCon :: DataCon -> Bool
+isUnboxableCon dc
+ | [t] <- dataConRepArgTys dc
+ , [t1] <- typeVt (scaledThing t)
+ = isUnboxable t1 &&
+ dataConTag dc == 1 &&
+ length (tyConDataCons $ dataConTyCon dc) == 1
+ | otherwise = False
+
+-- | one-constructor types with one primitive field represented as a JS Number
+-- can be unboxed
+isUnboxable :: VarType -> Bool
+isUnboxable DoubleV = True
+isUnboxable IntV = True -- includes Char#
+isUnboxable _ = False
+
+-- | Number of slots occupied by a PrimRep
+data SlotCount
+ = NoSlot
+ | OneSlot
+ | TwoSlots
+ deriving (Show,Eq,Ord)
+
+instance Outputable SlotCount where
+ ppr = text . show
+
+-- | Return SlotCount as an Int
+slotCount :: SlotCount -> Int
+slotCount = \case
+ NoSlot -> 0
+ OneSlot -> 1
+ TwoSlots -> 2
+
+
+-- | Number of slots occupied by a value with the given VarType
+varSize :: VarType -> Int
+varSize = slotCount . varSlotCount
+
+varSlotCount :: VarType -> SlotCount
+varSlotCount VoidV = NoSlot
+varSlotCount LongV = TwoSlots -- hi, low
+varSlotCount AddrV = TwoSlots -- obj/array, offset
+varSlotCount _ = OneSlot
+
+typeSize :: Type -> Int
+typeSize t = sum . map varSize . typeVt $ t
+
+isVoid :: VarType -> Bool
+isVoid VoidV = True
+isVoid _ = False
+
+isPtr :: VarType -> Bool
+isPtr PtrV = True
+isPtr _ = False
+
+isSingleVar :: VarType -> Bool
+isSingleVar v = varSlotCount v == OneSlot
+
+isMultiVar :: VarType -> Bool
+isMultiVar v = case varSlotCount v of
+ NoSlot -> False
+ OneSlot -> False
+ TwoSlots -> True
+
+-- | can we pattern match on these values in a case?
+isMatchable :: [VarType] -> Bool
+isMatchable [DoubleV] = True
+isMatchable [IntV] = True
+isMatchable _ = False
+
+tyConVt :: HasDebugCallStack => TyCon -> [VarType]
+tyConVt = typeVt . mkTyConTy
+
+idVt :: HasDebugCallStack => Id -> [VarType]
+idVt = typeVt . idType
+
+typeVt :: HasDebugCallStack => Type -> [VarType]
+typeVt t | isRuntimeRepKindedTy t = []
+typeVt t = map primRepVt (typePrimRep t)-- map uTypeVt (repTypeArgs t)
+
+-- only use if you know it's not an unboxed tuple
+uTypeVt :: HasDebugCallStack => UnaryType -> VarType
+uTypeVt ut
+ | isRuntimeRepKindedTy ut = VoidV
+-- | isRuntimeRepTy ut = VoidV
+ -- GHC panics on this otherwise
+ | Just (tc, ty_args) <- splitTyConApp_maybe ut
+ , length ty_args /= tyConArity tc = PtrV
+ | isPrimitiveType ut = (primTypeVt ut)
+ | otherwise =
+ case typePrimRep' ut of
+ [] -> VoidV
+ [pt] -> primRepVt pt
+ _ -> pprPanic "uTypeVt: not unary" (ppr ut)
+
+primRepVt :: HasDebugCallStack => PrimRep -> VarType
+primRepVt VoidRep = VoidV
+primRepVt LiftedRep = PtrV -- fixme does ByteArray# ever map to this?
+primRepVt UnliftedRep = RtsObjV
+primRepVt IntRep = IntV
+primRepVt Int8Rep = IntV
+primRepVt Int16Rep = IntV
+primRepVt Int32Rep = IntV
+primRepVt WordRep = IntV
+primRepVt Word8Rep = IntV
+primRepVt Word16Rep = IntV
+primRepVt Word32Rep = IntV
+primRepVt Int64Rep = LongV
+primRepVt Word64Rep = LongV
+primRepVt AddrRep = AddrV
+primRepVt FloatRep = DoubleV
+primRepVt DoubleRep = DoubleV
+primRepVt (VecRep{}) = error "uTypeVt: vector types are unsupported"
+
+typePrimRep' :: HasDebugCallStack => UnaryType -> [PrimRep]
+typePrimRep' ty = kindPrimRep' empty (typeKind ty)
+
+-- | Find the primitive representation of a 'TyCon'. Defined here to
+-- avoid module loops. Call this only on unlifted tycons.
+tyConPrimRep' :: HasDebugCallStack => TyCon -> [PrimRep]
+tyConPrimRep' tc = kindPrimRep' empty res_kind
+ where
+ res_kind = tyConResKind tc
+
+-- | Take a kind (of shape @TYPE rr@) and produce the 'PrimRep's
+-- of values of types of this kind.
+kindPrimRep' :: HasDebugCallStack => SDoc -> Kind -> [PrimRep]
+kindPrimRep' doc ki
+ | Just ki' <- coreView ki
+ = kindPrimRep' doc ki'
+kindPrimRep' doc (TyConApp _typ [runtime_rep])
+ = -- ASSERT( typ `hasKey` tYPETyConKey )
+ runtimeRepPrimRep doc runtime_rep
+kindPrimRep' doc ki
+ = pprPanic "kindPrimRep'" (ppr ki $$ doc)
+
+primTypeVt :: HasDebugCallStack => Type -> VarType
+primTypeVt t = case tyConAppTyCon_maybe (unwrapType t) of
+ Nothing -> error "primTypeVt: not a TyCon"
+ Just tc
+ | tc == charPrimTyCon -> IntV
+ | tc == intPrimTyCon -> IntV
+ | tc == wordPrimTyCon -> IntV
+ | tc == floatPrimTyCon -> DoubleV
+ | tc == doublePrimTyCon -> DoubleV
+ | tc == int8PrimTyCon -> IntV
+ | tc == word8PrimTyCon -> IntV
+ | tc == int16PrimTyCon -> IntV
+ | tc == word16PrimTyCon -> IntV
+ | tc == int32PrimTyCon -> IntV
+ | tc == word32PrimTyCon -> IntV
+ | tc == int64PrimTyCon -> LongV
+ | tc == word64PrimTyCon -> LongV
+ | tc == addrPrimTyCon -> AddrV
+ | tc == stablePtrPrimTyCon -> AddrV
+ | tc == stableNamePrimTyCon -> RtsObjV
+ | tc == statePrimTyCon -> VoidV
+ | tc == proxyPrimTyCon -> VoidV
+ | tc == realWorldTyCon -> VoidV
+ | tc == threadIdPrimTyCon -> RtsObjV
+ | tc == weakPrimTyCon -> RtsObjV
+ | tc == arrayPrimTyCon -> ArrV
+ | tc == smallArrayPrimTyCon -> ArrV
+ | tc == byteArrayPrimTyCon -> ObjV -- can contain any JS reference, used for JSVal
+ | tc == mutableArrayPrimTyCon -> ArrV
+ | tc == smallMutableArrayPrimTyCon -> ArrV
+ | tc == mutableByteArrayPrimTyCon -> ObjV -- can contain any JS reference, used for JSVal
+ | tc == mutVarPrimTyCon -> RtsObjV
+ | tc == mVarPrimTyCon -> RtsObjV
+ | tc == tVarPrimTyCon -> RtsObjV
+ | tc == bcoPrimTyCon -> RtsObjV -- unsupported?
+ | tc == stackSnapshotPrimTyCon -> RtsObjV
+ | tc == ioPortPrimTyCon -> RtsObjV -- unsupported?
+ | tc == anyTyCon -> PtrV
+ | tc == compactPrimTyCon -> ObjV -- unsupported?
+ | tc == eqPrimTyCon -> VoidV -- coercion token?
+ | tc == eqReprPrimTyCon -> VoidV -- role
+ | tc == unboxedUnitTyCon -> VoidV -- Void#
+ | otherwise -> PtrV -- anything else must be some boxed thing
+
+argVt :: StgArg -> VarType
+argVt a = uTypeVt . stgArgType $ a
+
+dataConType :: DataCon -> Type
+dataConType dc = idType (dataConWrapId dc)
+
+isBoolDataCon :: DataCon -> Bool
+isBoolDataCon dc = isBoolTy (dataConType dc)
+
+-- standard fixed layout: payload types
+-- payload starts at .d1 for heap objects, entry closest to Sp for stack frames
+fixedLayout :: [VarType] -> CILayout
+fixedLayout vts = CILayoutFixed (sum (map varSize vts)) vts
+
+-- 2-var values might have been moved around separately, use DoubleV as substitute
+-- ObjV is 1 var, so this is no problem for implicit metadata
+stackSlotType :: Id -> VarType
+stackSlotType i
+ | OneSlot <- varSlotCount otype = otype
+ | otherwise = DoubleV
+ where otype = uTypeVt (idType i)
+
+idPrimReps :: Id -> [PrimRep]
+idPrimReps = typePrimReps . idType
+
+typePrimReps :: Type -> [PrimRep]
+typePrimReps = typePrimRep . unwrapType
+
+primRepSize :: PrimRep -> SlotCount
+primRepSize p = varSlotCount (primRepVt p)
+
+-- | Associate the given values to each RrimRep in the given order, taking into
+-- account the number of slots per PrimRep
+assocPrimReps :: Outputable a => [PrimRep] -> [a] -> [(PrimRep, [a])]
+assocPrimReps [] _ = []
+assocPrimReps (r:rs) vs = case (primRepSize r,vs) of
+ (NoSlot, xs) -> (r,[]) : assocPrimReps rs xs
+ (OneSlot, x:xs) -> (r,[x]) : assocPrimReps rs xs
+ (TwoSlots, x:y:xs) -> (r,[x,y]) : assocPrimReps rs xs
+ err -> pprPanic "assocPrimReps" (ppr err)
+
+-- | Associate the given values to the Id's PrimReps, taking into account the
+-- number of slots per PrimRep
+assocIdPrimReps :: Outputable a => Id -> [a] -> [(PrimRep, [a])]
+assocIdPrimReps i = assocPrimReps (idPrimReps i)
+
+-- | Associate the given JExpr to the Id's PrimReps, taking into account the
+-- number of slots per PrimRep
+assocIdExprs :: Id -> [JExpr] -> [TypedExpr]
+assocIdExprs i es = fmap (uncurry TypedExpr) (assocIdPrimReps i es)
+
+-- | Return False only if we are *sure* it's a data type
+-- Look through newtypes etc as much as possible
+might_be_a_function :: HasDebugCallStack => Type -> Bool
+might_be_a_function ty
+ | [LiftedRep] <- typePrimRep ty
+ , Just tc <- tyConAppTyCon_maybe (unwrapType ty)
+ , isDataTyCon tc
+ = False
+ | otherwise
+ = True
+
+mkArityTag :: Int -> Int -> Int
+mkArityTag arity registers = arity Bits..|. (registers `Bits.shiftL` 8)
+
+toTypeList :: [VarType] -> [Int]
+toTypeList = concatMap (\x -> replicate (varSize x) (fromEnum x))
diff --git a/compiler/GHC/StgToJS/DataCon.hs b/compiler/GHC/StgToJS/DataCon.hs
new file mode 100644
index 0000000000..242ea7f189
--- /dev/null
+++ b/compiler/GHC/StgToJS/DataCon.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.DataCon
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Code generation of data constructors
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.DataCon
+ ( genCon
+ , allocCon
+ , allocUnboxedCon
+ , allocDynamicE
+ , allocDynamic
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Closure
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.Types
+import GHC.StgToJS.Monad
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Utils
+import GHC.StgToJS.Ids
+
+import GHC.Core.DataCon
+
+import GHC.Types.CostCentre
+import GHC.Types.Unique.Map
+
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+import GHC.Data.FastString
+
+import Data.Maybe
+
+-- | Generate a data constructor. Special handling for unboxed tuples
+genCon :: ExprCtx -> DataCon -> [JExpr] -> G JStat
+genCon ctx con args
+ | isUnboxedTupleDataCon con
+ = return $ assignToExprCtx ctx args
+
+ | [ValExpr (JVar ctxi)] <- concatMap typex_expr (ctxTarget ctx)
+ = allocCon ctxi con currentCCS args
+
+ | xs <- concatMap typex_expr (ctxTarget ctx)
+ = pprPanic "genCon: unhandled DataCon" (ppr (con, args, xs))
+
+-- | Allocate a data constructor. Allocate in this context means bind the data
+-- constructor to 'to'
+allocCon :: Ident -> DataCon -> CostCentreStack -> [JExpr] -> G JStat
+allocCon to con cc xs
+ | isBoolDataCon con || isUnboxableCon con =
+ return (toJExpr to |= allocUnboxedCon con xs)
+{- | null xs = do
+ i <- varForId (dataConWorkId con)
+ return (assignj to i) -}
+ | otherwise = do
+ e <- varForDataConWorker con
+ cs <- getSettings
+ prof <- profiling
+ ccsJ <- if prof then ccsVarJ cc else return Nothing
+ return $ allocDynamic cs False to e xs ccsJ
+
+-- | Allocate an unboxed data constructor. If we have a bool we calculate the
+-- right value. If not then we expect a singleton list and unbox by converting
+-- ''C x' to 'x'. NB. This function may panic.
+allocUnboxedCon :: DataCon -> [JExpr] -> JExpr
+allocUnboxedCon con = \case
+ []
+ | isBoolDataCon con && dataConTag con == 1 -> false_
+ | isBoolDataCon con && dataConTag con == 2 -> true_
+ [x]
+ | isUnboxableCon con -> x
+ xs -> pprPanic "allocUnboxedCon: not an unboxed constructor" (ppr (con,xs))
+
+-- | Allocate an entry function. See 'GHC.StgToJS.hs' for the object layout.
+allocDynamicE :: Bool -- ^ csInlineAlloc from StgToJSConfig
+ -> JExpr
+ -> [JExpr]
+ -> Maybe JExpr
+ -> JExpr
+allocDynamicE inline_alloc entry free cc
+ | inline_alloc || length free > 24 = newClosure $ Closure
+ { clEntry = entry
+ , clField1 = fillObj1
+ , clField2 = fillObj2
+ , clMeta = ValExpr (JInt 0)
+ , clCC = cc
+ }
+ | otherwise = ApplExpr allocFun (toJExpr entry : free ++ maybeToList cc)
+ where
+ allocFun = allocClsA (length free)
+ (fillObj1,fillObj2)
+ = case free of
+ [] -> (null_, null_)
+ [x] -> (x,null_)
+ [x,y] -> (x,y)
+ (x:xs) -> (x,toJExpr (JHash $ listToUniqMap (zip dataFields xs)))
+ dataFields = map (mkFastString . ('d':) . show) [(1::Int)..]
+
+-- | Allocate a dynamic object
+allocDynamic :: StgToJSConfig -> Bool -> Ident -> JExpr -> [JExpr] -> Maybe JExpr -> JStat
+allocDynamic s need_decl to entry free cc
+ | need_decl = DeclStat to (Just value)
+ | otherwise = toJExpr to |= value
+ where
+ value = allocDynamicE (csInlineAlloc s) entry free cc
diff --git a/compiler/GHC/StgToJS/Deps.hs b/compiler/GHC/StgToJS/Deps.hs
new file mode 100644
index 0000000000..229daf51a4
--- /dev/null
+++ b/compiler/GHC/StgToJS/Deps.hs
@@ -0,0 +1,191 @@
+{-# LANGUAGE TupleSections #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Deps
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Module to calculate the transitive dependencies of a module
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Deps
+ ( genDependencyData
+ )
+where
+
+import GHC.Prelude
+
+import GHC.StgToJS.Object as Object
+import GHC.StgToJS.Types
+import GHC.StgToJS.Ids
+
+import GHC.JS.Syntax
+
+import GHC.Types.Id
+import GHC.Types.Unique
+import GHC.Types.Unique.FM
+import GHC.Types.Name
+
+import GHC.Unit.Module
+
+import GHC.Utils.Misc
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+
+import GHC.Data.FastString
+
+import Data.Map (Map)
+import qualified Data.Map as M
+import qualified Data.Set as S
+import qualified Data.IntSet as IS
+import qualified Data.IntMap as IM
+import Data.IntMap (IntMap)
+import Data.Array
+import Data.Either
+import Control.Monad
+
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.State
+
+data DependencyDataCache = DDC
+ { ddcModule :: !(IntMap Unit) -- ^ Unique Module -> Unit
+ , ddcId :: !(IntMap Object.ExportedFun) -- ^ Unique Id -> Object.ExportedFun (only to other modules)
+ , ddcOther :: !(Map OtherSymb Object.ExportedFun)
+ }
+
+-- | Generate module dependency data
+--
+-- Generate the object's dependency data, taking care that package and module names
+-- are only stored once
+genDependencyData
+ :: HasDebugCallStack
+ => Module
+ -> [LinkableUnit]
+ -> G Object.Deps
+genDependencyData mod units = do
+ -- [(blockindex, blockdeps, required, exported)]
+ ds <- evalStateT (mapM (uncurry oneDep) blocks)
+ (DDC IM.empty IM.empty M.empty)
+ return $ Object.Deps
+ { depsModule = mod
+ , depsRequired = IS.fromList [ n | (n, _, True, _) <- ds ]
+ , depsHaskellExported = M.fromList $ (\(n,_,_,es) -> map (,n) es) =<< ds
+ , depsBlocks = listArray (0, length blocks-1) (map (\(_,deps,_,_) -> deps) ds)
+ }
+ where
+ -- Id -> Block
+ unitIdExports :: UniqFM Id Int
+ unitIdExports = listToUFM $
+ concatMap (\(u,n) -> map (,n) (luIdExports u)) blocks
+
+ -- OtherSymb -> Block
+ unitOtherExports :: Map OtherSymb Int
+ unitOtherExports = M.fromList $
+ concatMap (\(u,n) -> map (,n)
+ (map (OtherSymb mod)
+ (luOtherExports u)))
+ blocks
+
+ blocks :: [(LinkableUnit, Int)]
+ blocks = zip units [0..]
+
+ -- generate the list of exports and set of dependencies for one unit
+ oneDep :: LinkableUnit
+ -> Int
+ -> StateT DependencyDataCache G (Int, Object.BlockDeps, Bool, [Object.ExportedFun])
+ oneDep (LinkableUnit _ idExports otherExports idDeps pseudoIdDeps otherDeps req _frefs) n = do
+ (edi, bdi) <- partitionEithers <$> mapM (lookupIdFun n) idDeps
+ (edo, bdo) <- partitionEithers <$> mapM lookupOtherFun otherDeps
+ (edp, bdp) <- partitionEithers <$> mapM (lookupPseudoIdFun n) pseudoIdDeps
+ expi <- mapM lookupExportedId (filter isExportedId idExports)
+ expo <- mapM lookupExportedOther otherExports
+ -- fixme thin deps, remove all transitive dependencies!
+ let bdeps = Object.BlockDeps
+ (IS.toList . IS.fromList . filter (/=n) $ bdi++bdo++bdp)
+ (S.toList . S.fromList $ edi++edo++edp)
+ return (n, bdeps, req, expi++expo)
+
+ idModule :: Id -> Maybe Module
+ idModule i = nameModule_maybe (getName i) >>= \m ->
+ guard (m /= mod) >> return m
+
+ lookupPseudoIdFun :: Int -> Unique
+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)
+ lookupPseudoIdFun _n u =
+ case lookupUFM_Directly unitIdExports u of
+ Just k -> return (Right k)
+ _ -> panic "lookupPseudoIdFun"
+
+ -- get the function for an Id from the cache, add it if necessary
+ -- result: Left Object.ExportedFun if function refers to another module
+ -- Right blockNumber if function refers to current module
+ --
+ -- assumes function is internal to the current block if it's
+ -- from teh current module and not in the unitIdExports map.
+ lookupIdFun :: Int -> Id
+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)
+ lookupIdFun n i = case lookupUFM unitIdExports i of
+ Just k -> return (Right k)
+ Nothing -> case idModule i of
+ Nothing -> return (Right n)
+ Just m ->
+ let k = getKey . getUnique $ i
+ addEntry :: StateT DependencyDataCache G Object.ExportedFun
+ addEntry = do
+ (TxtI idTxt) <- lift (identForId i)
+ lookupExternalFun (Just k) (OtherSymb m idTxt)
+ in if m == mod
+ then pprPanic "local id not found" (ppr m)
+ else Left <$> do
+ mr <- gets (IM.lookup k . ddcId)
+ maybe addEntry return mr
+
+ -- get the function for an OtherSymb from the cache, add it if necessary
+ lookupOtherFun :: OtherSymb
+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)
+ lookupOtherFun od@(OtherSymb m idTxt) =
+ case M.lookup od unitOtherExports of
+ Just n -> return (Right n)
+ Nothing | m == mod -> panic ("genDependencyData.lookupOtherFun: unknown local other id: " ++ unpackFS idTxt)
+ Nothing -> Left <$> (maybe (lookupExternalFun Nothing od) return =<<
+ gets (M.lookup od . ddcOther))
+
+ lookupExportedId :: Id -> StateT DependencyDataCache G Object.ExportedFun
+ lookupExportedId i = do
+ (TxtI idTxt) <- lift (identForId i)
+ lookupExternalFun (Just . getKey . getUnique $ i) (OtherSymb mod idTxt)
+
+ lookupExportedOther :: FastString -> StateT DependencyDataCache G Object.ExportedFun
+ lookupExportedOther = lookupExternalFun Nothing . OtherSymb mod
+
+ -- lookup a dependency to another module, add to the id cache if there's
+ -- an id key, otherwise add to other cache
+ lookupExternalFun :: Maybe Int
+ -> OtherSymb -> StateT DependencyDataCache G Object.ExportedFun
+ lookupExternalFun mbIdKey od@(OtherSymb m idTxt) = do
+ let mk = getKey . getUnique $ m
+ mpk = moduleUnit m
+ exp_fun = Object.ExportedFun m (LexicalFastString idTxt)
+ addCache = do
+ ms <- gets ddcModule
+ let !cache' = IM.insert mk mpk ms
+ modify (\s -> s { ddcModule = cache'})
+ pure exp_fun
+ f <- do
+ mbm <- gets (IM.member mk . ddcModule)
+ case mbm of
+ False -> addCache
+ True -> pure exp_fun
+
+ case mbIdKey of
+ Nothing -> modify (\s -> s { ddcOther = M.insert od f (ddcOther s) })
+ Just k -> modify (\s -> s { ddcId = IM.insert k f (ddcId s) })
+
+ return f
diff --git a/compiler/GHC/StgToJS/Expr.hs b/compiler/GHC/StgToJS/Expr.hs
new file mode 100644
index 0000000000..fd6d09585f
--- /dev/null
+++ b/compiler/GHC/StgToJS/Expr.hs
@@ -0,0 +1,1045 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveFunctor #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Expr
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Code generation of Expressions
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Expr
+ ( genExpr
+ , genEntryType
+ , loadLiveFun
+ , genStaticRefsRhs
+ , genStaticRefs
+ , genBody
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Apply
+import GHC.StgToJS.Arg
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.FFI
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Monad
+import GHC.StgToJS.DataCon
+import GHC.StgToJS.Types
+import GHC.StgToJS.Literal
+import GHC.StgToJS.Prim
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Regs
+import GHC.StgToJS.StgUtils
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Utils
+import GHC.StgToJS.Stack
+import GHC.StgToJS.Ids
+
+import GHC.Types.Basic
+import GHC.Types.CostCentre
+import GHC.Types.Tickish
+import GHC.Types.Var.Set
+import GHC.Types.Id
+import GHC.Types.Unique.FM
+import GHC.Types.RepType
+
+import GHC.Stg.Syntax
+import GHC.Stg.Utils
+
+import GHC.Builtin.PrimOps
+
+import GHC.Core
+import GHC.Core.TyCon
+import GHC.Core.DataCon
+import GHC.Core.Opt.Arity (isOneShotBndr)
+import GHC.Core.Type hiding (typeSize)
+
+import GHC.Utils.Misc
+import GHC.Utils.Monad
+import GHC.Utils.Panic
+import GHC.Utils.Panic.Plain
+import GHC.Utils.Outputable (ppr, renderWithContext, defaultSDocContext)
+import qualified Control.Monad.Trans.State.Strict as State
+import GHC.Data.FastString
+import qualified GHC.Data.List.SetOps as ListSetOps
+
+import Data.Monoid
+import Data.Maybe
+import Data.Function
+import Data.Either
+import qualified Data.List as L
+import qualified Data.Set as S
+import qualified Data.Map as M
+import Control.Monad
+import Control.Arrow ((&&&))
+
+-- | Evaluate an expression in the given expression context (continuation)
+genExpr :: HasDebugCallStack => ExprCtx -> CgStgExpr -> G (JStat, ExprResult)
+genExpr ctx stg = case stg of
+ StgApp f args -> genApp ctx f args
+ StgLit l -> do
+ ls <- genLit l
+ let r = assignToExprCtx ctx ls
+ pure (r,ExprInline Nothing)
+ StgConApp con _n args _ -> do
+ as <- concatMapM genArg args
+ c <- genCon ctx con as
+ return (c, ExprInline (Just as))
+ StgOpApp (StgFCallOp f _) args t
+ -> genForeignCall ctx f t (concatMap typex_expr $ ctxTarget ctx) args
+ StgOpApp (StgPrimOp op) args t
+ -> genPrimOp ctx op args t
+ StgOpApp (StgPrimCallOp c) args t
+ -> genPrimCall ctx c args t
+ StgCase e b at alts
+ -> genCase ctx b e at alts (liveVars $ stgExprLive False stg)
+ StgLet _ b e -> do
+ (b',ctx') <- genBind ctx b
+ (s,r) <- genExpr ctx' e
+ return (b' <> s, r)
+ StgLetNoEscape _ b e -> do
+ (b', ctx') <- genBindLne ctx b
+ (s, r) <- genExpr ctx' e
+ return (b' <> s, r)
+ StgTick (ProfNote cc count scope) e -> do
+ setSCCstats <- ifProfilingM $ setCC cc count scope
+ (stats, result) <- genExpr ctx e
+ return (setSCCstats <> stats, result)
+ StgTick (SourceNote span _sname) e
+ -> genExpr (ctxSetSrcSpan span ctx) e
+ StgTick _m e
+ -> genExpr ctx e
+
+-- | regular let binding: allocate heap object
+genBind :: HasDebugCallStack
+ => ExprCtx
+ -> CgStgBinding
+ -> G (JStat, ExprCtx)
+genBind ctx bndr =
+ case bndr of
+ StgNonRec b r -> do
+ j <- assign b r >>= \case
+ Just ja -> return ja
+ Nothing -> allocCls Nothing [(b,r)]
+ return (j, addEvalRhs ctx [(b,r)])
+ StgRec bs -> do
+ jas <- mapM (uncurry assign) bs -- fixme these might depend on parts initialized by allocCls
+ let m = if null jas then Nothing else Just (mconcat $ catMaybes jas)
+ j <- allocCls m . map snd . filter (isNothing . fst) $ zip jas bs
+ return (j, addEvalRhs ctx bs)
+ where
+ ctx' = ctxClearLneFrame ctx
+
+ assign :: Id -> CgStgRhs -> G (Maybe JStat)
+ assign b (StgRhsClosure _ _ccs {-[the_fv]-} _upd [] expr)
+ | let strip = snd . stripStgTicksTop (not . tickishIsCode)
+ , StgCase (StgApp scrutinee []) _ (AlgAlt _) [GenStgAlt (DataAlt _) params sel_expr] <- strip expr
+ , StgApp selectee [] <- strip sel_expr
+ , let params_w_offsets = zip params (L.scanl' (+) 1 $ map (typeSize . idType) params)
+ , let total_size = sum (map (typeSize . idType) params)
+ -- , the_fv == scrutinee -- fixme check
+ , Just the_offset <- ListSetOps.assocMaybe params_w_offsets selectee
+ , the_offset <= 16 -- fixme make this some configurable constant
+ = do
+ let the_fv = scrutinee -- error "the_fv" -- fixme
+ let sel_tag | the_offset == 2 = if total_size == 2 then "2a"
+ else "2b"
+ | otherwise = show the_offset
+ tgts <- identsForId b
+ the_fvjs <- varsForId the_fv
+ case (tgts, the_fvjs) of
+ ([tgt], [the_fvj]) -> return $ Just
+ (tgt ||= ApplExpr (var ("h$c_sel_" <> mkFastString sel_tag)) [the_fvj])
+ _ -> panic "genBind.assign: invalid size"
+ assign b (StgRhsClosure _ext _ccs _upd [] expr)
+ | snd (isInlineExpr (ctxEvaluatedIds ctx) expr) = do
+ d <- declVarsForId b
+ tgt <- varsForId b
+ let ctx' = ctx { ctxTarget = assocIdExprs b tgt }
+ (j, _) <- genExpr ctx' expr
+ return (Just (d <> j))
+ assign _b StgRhsCon{} = return Nothing
+ assign b r = genEntry ctx' b r >> return Nothing
+
+ addEvalRhs c [] = c
+ addEvalRhs c ((b,r):xs)
+ | StgRhsCon{} <- r = addEvalRhs (ctxAssertEvaluated b c) xs
+ | (StgRhsClosure _ _ ReEntrant _ _) <- r = addEvalRhs (ctxAssertEvaluated b c) xs
+ | otherwise = addEvalRhs c xs
+
+genBindLne :: HasDebugCallStack
+ => ExprCtx
+ -> CgStgBinding
+ -> G (JStat, ExprCtx)
+genBindLne ctx bndr = do
+ -- compute live variables and the offsets where they will be stored in the
+ -- stack
+ vis <- map (\(x,y,_) -> (x,y)) <$>
+ optimizeFree oldFrameSize (newLvs++map fst updBinds)
+ -- initialize updatable bindings to null_
+ declUpds <- mconcat <$> mapM (fmap (||= null_) . identForId . fst) updBinds
+ -- update expression context to include the updated LNE frame
+ let ctx' = ctxUpdateLneFrame vis bound ctx
+ mapM_ (uncurry $ genEntryLne ctx') binds
+ return (declUpds, ctx')
+ where
+ oldFrameSize = ctxLneFrameSize ctx
+ isOldLv i = ctxIsLneBinding ctx i ||
+ ctxIsLneLiveVar ctx i
+ live = liveVars $ mkDVarSet $ stgLneLive' bndr
+ newLvs = filter (not . isOldLv) (dVarSetElems live)
+ binds = case bndr of
+ StgNonRec b e -> [(b,e)]
+ StgRec bs -> bs
+ bound = map fst binds
+ (updBinds, _nonUpdBinds) = L.partition (isUpdatableRhs . snd) binds
+
+-- | Generate let-no-escape entry
+--
+-- Let-no-escape entries live on the stack. There is no heap object associated with them.
+--
+-- A let-no-escape entry is called like a normal stack frame, although as an optimization,
+-- `Stack`[`Sp`] is not set when making the call. This is done later if the
+-- thread needs to be suspended.
+--
+-- Updatable let-no-escape binders have one 'private' slot in the stack frame. This slot
+-- is initially set to null, changed to h$blackhole when the thunk is being evaluated.
+--
+genEntryLne :: HasDebugCallStack => ExprCtx -> Id -> CgStgRhs -> G ()
+genEntryLne ctx i rhs@(StgRhsClosure _ext _cc update args body) =
+ resetSlots $ do
+ let payloadSize = ctxLneFrameSize ctx
+ vars = ctxLneFrameVars ctx
+ myOffset =
+ maybe (panic "genEntryLne: updatable binder not found in let-no-escape frame")
+ ((payloadSize-) . fst)
+ (L.find ((==i) . fst . snd) (zip [0..] vars))
+ bh | isUpdatable update =
+ jVar (\x -> mconcat
+ [ x |= ApplExpr (var "h$bh_lne") [Sub sp (toJExpr myOffset), toJExpr (payloadSize+1)]
+ , IfStat x (ReturnStat x) mempty
+ ])
+ | otherwise = mempty
+ lvs <- popLneFrame True payloadSize ctx
+ body <- genBody ctx i R1 args body
+ ei@(TxtI eii) <- identForEntryId i
+ sr <- genStaticRefsRhs rhs
+ let f = JFunc [] (bh <> lvs <> body)
+ emitClosureInfo $
+ ClosureInfo ei
+ (CIRegs 0 $ concatMap idVt args)
+ (eii <> ", " <> mkFastString (renderWithContext defaultSDocContext (ppr i)))
+ (fixedLayout . reverse $
+ map (stackSlotType . fst) (ctxLneFrameVars ctx))
+ CIStackFrame
+ sr
+ emitToplevel (ei ||= toJExpr f)
+genEntryLne ctx i (StgRhsCon cc con _mu _ticks args) = resetSlots $ do
+ let payloadSize = ctxLneFrameSize ctx
+ ei@(TxtI _eii) <- identForEntryId i
+ -- di <- varForDataConWorker con
+ ii <- freshIdent
+ p <- popLneFrame True payloadSize ctx
+ args' <- concatMapM genArg args
+ ac <- allocCon ii con cc args'
+ emitToplevel (ei ||= toJExpr (JFunc []
+ (mconcat [decl ii, p, ac, r1 |= toJExpr ii, returnStack])))
+
+-- | Generate the entry function for a local closure
+genEntry :: HasDebugCallStack => ExprCtx -> Id -> CgStgRhs -> G ()
+genEntry _ _i StgRhsCon {} = return ()
+genEntry ctx i rhs@(StgRhsClosure _ext cc {-_bi live-} upd_flag args body) = resetSlots $ do
+ let live = stgLneLiveExpr rhs -- error "fixme" -- probably find live vars in body
+ ll <- loadLiveFun live
+ llv <- verifyRuntimeReps live
+ upd <- genUpdFrame upd_flag i
+ body <- genBody entryCtx i R2 args body
+ ei@(TxtI eii) <- identForEntryId i
+ et <- genEntryType args
+ setcc <- ifProfiling $
+ if et == CIThunk
+ then enterCostCentreThunk
+ else enterCostCentreFun cc
+ sr <- genStaticRefsRhs rhs
+ emitClosureInfo $ ClosureInfo ei
+ (CIRegs 0 $ PtrV : concatMap idVt args)
+ (eii <> ", " <> mkFastString (renderWithContext defaultSDocContext (ppr i)))
+ (fixedLayout $ map (uTypeVt . idType) live)
+ et
+ sr
+ emitToplevel (ei ||= toJExpr (JFunc [] (mconcat [ll, llv, upd, setcc, body])))
+ where
+ entryCtx = ctxSetTarget [] (ctxClearLneFrame ctx)
+
+-- | Generate the entry function types for identifiers. Note that this only
+-- returns either 'CIThunk' or 'CIFun'. Everything else (PAP Blackhole etc.) is
+-- filtered as not a RuntimeRepKinded type.
+genEntryType :: HasDebugCallStack => [Id] -> G CIType
+genEntryType [] = return CIThunk
+genEntryType args0 = do
+ args' <- mapM genIdArg args
+ return $ CIFun (length args) (length $ concat args')
+ where
+ args = filter (not . isRuntimeRepKindedTy . idType) args0
+
+-- | Generate the body of an object
+genBody :: HasDebugCallStack
+ => ExprCtx
+ -> Id
+ -> StgReg
+ -> [Id]
+ -> CgStgExpr
+ -> G JStat
+genBody ctx i startReg args e = do
+ -- load arguments into local variables
+ la <- do
+ args' <- concatMapM genIdArgI args
+ return (declAssignAll args' (fmap toJExpr [startReg..]))
+
+ -- assert that arguments have valid runtime reps
+ lav <- verifyRuntimeReps args
+
+ -- compute PrimReps and their number of slots required to return the result of
+ -- i applied to args.
+ let res_vars = resultSize args i
+
+ -- compute typed expressions for each slot and assign registers
+ let go_var regs = \case
+ [] -> []
+ ((rep,size):rs) ->
+ let !(regs0,regs1) = splitAt size regs
+ !ts = go_var regs1 rs
+ in TypedExpr rep regs0 : ts
+
+ let tgt = go_var jsRegsFromR1 res_vars
+ let !ctx' = ctx { ctxTarget = tgt }
+
+ -- generate code for the expression
+ (e, _r) <- genExpr ctx' e
+
+ return $ la <> lav <> e <> returnStack
+
+-- | Find the result type after applying the function to the arguments
+--
+-- It's trickier than it looks because:
+--
+-- 1. we don't have the Arity of the Id. The following functions return
+-- different values in some cases:
+-- - idArity
+-- - typeArity . idType
+-- - idFunRepArity
+-- - typeArity . unwrapType . idType
+-- Moreover the number of args may be different than all of these arities
+--
+-- 2. sometimes the type is Any, perhaps after some unwrapping. For example
+-- HappyAbsSyn is a newtype around HappyAny which is Any or (forall a. a).
+--
+-- Se we're left to use the applied arguments to peel the type (unwrapped) one
+-- arg at a time. But passed args are args after unarisation so we need to
+-- unarise every argument type that we peel (using typePrimRepArgs) to get the
+-- number of passed args consumed by each type arg.
+--
+-- In case of failure to determine the type, we default to LiftedRep as it's
+-- probably what it is.
+--
+resultSize :: HasDebugCallStack => [Id] -> Id -> [(PrimRep, Int)]
+resultSize args i = result
+ where
+ result = result_reps `zip` result_slots
+ result_slots = fmap (slotCount . primRepSize) result_reps
+ result_reps = trim_args (unwrapType (idType i)) (length args)
+
+ trim_args t 0 = typePrimRep t
+ trim_args t n
+ | Just (_af, _mult, arg, res) <- splitFunTy_maybe t
+ , nargs <- length (typePrimRepArgs arg)
+ , assert (n >= nargs) True
+ = trim_args (unwrapType res) (n - nargs)
+ | otherwise
+ = pprTrace "result_type: not a function type, assume LiftedRep" (ppr t)
+ [LiftedRep]
+
+-- | Ensure that the set of identifiers has valid 'RuntimeRep's. This function
+-- returns a no-op when 'csRuntimeAssert' in 'StgToJSConfig' is False.
+verifyRuntimeReps :: HasDebugCallStack => [Id] -> G JStat
+verifyRuntimeReps xs = do
+ runtime_assert <- csRuntimeAssert <$> getSettings
+ if not runtime_assert
+ then pure mempty
+ else mconcat <$> mapM verifyRuntimeRep xs
+ where
+ verifyRuntimeRep i = do
+ i' <- varsForId i
+ pure $ go i' (idVt i)
+ go js (VoidV:vs) = go js vs
+ go (j1:j2:js) (LongV:vs) = v "h$verify_rep_long" [j1,j2] <> go js vs
+ go (j1:j2:js) (AddrV:vs) = v "h$verify_rep_addr" [j1,j2] <> go js vs
+ go (j:js) (v:vs) = ver j v <> go js vs
+ go [] [] = mempty
+ go _ _ = pprPanic "verifyRuntimeReps: inconsistent sizes" (ppr xs)
+ ver j PtrV = v "h$verify_rep_heapobj" [j]
+ ver j IntV = v "h$verify_rep_int" [j]
+ ver j RtsObjV = v "h$verify_rep_rtsobj" [j]
+ ver j DoubleV = v "h$verify_rep_double" [j]
+ ver j ArrV = v "h$verify_rep_arr" [j]
+ ver _ _ = mempty
+ v f as = ApplStat (var f) as
+
+-- | Given a set of 'Id's, bind each 'Id' to the appropriate data fields in N
+-- registers. This assumes these data fields have already been populated in the
+-- registers. For the empty, singleton, and binary case use register 1, for any
+-- more use as many registers as necessary.
+loadLiveFun :: [Id] -> G JStat
+loadLiveFun l = do
+ l' <- concat <$> mapM identsForId l
+ case l' of
+ [] -> return mempty
+ -- set the ident to d1 field of register 1
+ [v] -> return (v ||= r1 .^ closureField1_)
+ -- set the idents to d1 and d2 fields of register 1
+ [v1,v2] -> return $ mconcat
+ [ v1 ||= r1 .^ closureField1_
+ , v2 ||= r1 .^ closureField2_
+ ]
+ -- and so on
+ (v:vs) -> do
+ d <- freshIdent
+ let l'' = mconcat . zipWith (loadLiveVar $ toJExpr d) [(1::Int)..] $ vs
+ return $ mconcat
+ [ v ||= r1 .^ closureField1_
+ , d ||= r1 .^ closureField2_
+ , l''
+ ]
+ where
+ loadLiveVar d n v = let ident = TxtI (dataFieldName n)
+ in v ||= SelExpr d ident
+
+-- | Pop a let-no-escape frame off the stack
+popLneFrame :: Bool -> Int -> ExprCtx -> G JStat
+popLneFrame inEntry size ctx = do
+ -- calculate the new stack size
+ let ctx' = ctxLneShrinkStack ctx size
+
+ let gen_id_slot (i,n) = do
+ ids <- identsForId i
+ let !id_n = ids !! (n-1)
+ pure (id_n, SlotId i n)
+
+ is <- mapM gen_id_slot (ctxLneFrameVars ctx')
+
+ let skip = if inEntry then 1 else 0 -- pop the frame header
+ popSkipI skip is
+
+-- | Generate an updated given an 'Id'
+genUpdFrame :: UpdateFlag -> Id -> G JStat
+genUpdFrame u i
+ | isReEntrant u = pure mempty
+ | isOneShotBndr i = maybeBh
+ | isUpdatable u = updateThunk
+ | otherwise = maybeBh
+ where
+ isReEntrant ReEntrant = True
+ isReEntrant _ = False
+ maybeBh = do
+ settings <- getSettings
+ assertRtsStat (return $ bhSingleEntry settings)
+
+-- | Blackhole single entry
+--
+-- Overwrite a single entry object with a special thunk that behaves like a
+-- black hole (throws a JS exception when entered) but pretends to be a thunk.
+-- Useful for making sure that the object is not accidentally entered multiple
+-- times
+--
+bhSingleEntry :: StgToJSConfig -> JStat
+bhSingleEntry _settings = mconcat
+ [ r1 .^ closureEntry_ |= var "h$blackholeTrap"
+ , r1 .^ closureField1_ |= undefined_
+ , r1 .^ closureField2_ |= undefined_
+ ]
+
+genStaticRefsRhs :: CgStgRhs -> G CIStatic
+genStaticRefsRhs lv = genStaticRefs (stgRhsLive lv)
+
+-- fixme, update to new way to compute static refs dynamically
+genStaticRefs :: LiveVars -> G CIStatic
+genStaticRefs lv
+ | isEmptyDVarSet sv = return (CIStaticRefs [])
+ | otherwise = do
+ unfloated <- State.gets gsUnfloated
+ let xs = filter (\x -> not (elemUFM x unfloated ||
+ typeLevity_maybe (idType x) == Just Unlifted))
+ (dVarSetElems sv)
+ CIStaticRefs . catMaybes <$> mapM getStaticRef xs
+ where
+ sv = liveStatic lv
+
+ getStaticRef :: Id -> G (Maybe FastString)
+ getStaticRef = fmap (fmap itxt . listToMaybe) . identsForId
+
+-- | Reorder the things we need to push to reuse existing stack values as much
+-- as possible True if already on the stack at that location
+optimizeFree
+ :: HasDebugCallStack
+ => Int
+ -> [Id]
+ -> G [(Id,Int,Bool)] -- ^ A list of stack slots.
+ -- -- Id: stored on the slot
+ -- -- Int: the part of the value that is stored
+ -- -- Bool: True when the slot already contains a value
+optimizeFree offset ids = do
+ -- this line goes wrong vvvvvvv
+ let -- ids' = concat $ map (\i -> map (i,) [1..varSize . uTypeVt . idType $ i]) ids
+ idSize :: Id -> Int
+ idSize i = sum $ map varSize (typeVt . idType $ i)
+ ids' = concatMap (\i -> map (i,) [1..idSize i]) ids
+ -- 1..varSize] . uTypeVt . idType $ i]) (typeVt ids)
+ l = length ids'
+ slots <- drop offset . take l . (++repeat SlotUnknown) <$> getSlots
+ let slm = M.fromList (zip slots [0..])
+ (remaining, fixed) = partitionEithers $
+ map (\inp@(i,n) -> maybe (Left inp) (\j -> Right (i,n,j,True))
+ (M.lookup (SlotId i n) slm)) ids'
+ takenSlots = S.fromList (fmap (\(_,_,x,_) -> x) fixed)
+ freeSlots = filter (`S.notMember` takenSlots) [0..l-1]
+ remaining' = zipWith (\(i,n) j -> (i,n,j,False)) remaining freeSlots
+ allSlots = L.sortBy (compare `on` \(_,_,x,_) -> x) (fixed ++ remaining')
+ return $ map (\(i,n,_,b) -> (i,n,b)) allSlots
+
+-- | Allocate local closures
+allocCls :: Maybe JStat -> [(Id, CgStgRhs)] -> G JStat
+allocCls dynMiddle xs = do
+ (stat, dyn) <- partitionEithers <$> mapM toCl xs
+ ac <- allocDynAll True dynMiddle dyn
+ pure (mconcat stat <> ac)
+ where
+ -- left = static, right = dynamic
+ toCl :: (Id, CgStgRhs)
+ -> G (Either JStat (Ident,JExpr,[JExpr],CostCentreStack))
+ -- statics
+ {- making zero-arg constructors static is problematic, see #646
+ proper candidates for this optimization should have been floated
+ already
+ toCl (i, StgRhsCon cc con []) = do
+ ii <- identForId i
+ Left <$> (return (decl ii) <> allocCon ii con cc []) -}
+ toCl (i, StgRhsCon cc con _mui _ticjs [a]) | isUnboxableCon con = do
+ ii <- identForId i
+ ac <- allocCon ii con cc =<< genArg a
+ pure (Left (decl ii <> ac))
+
+ -- dynamics
+ toCl (i, StgRhsCon cc con _mu _ticks ar) =
+ -- fixme do we need to handle unboxed?
+ Right <$> ((,,,) <$> identForId i
+ <*> varForDataConWorker con
+ <*> concatMapM genArg ar
+ <*> pure cc)
+ toCl (i, cl@(StgRhsClosure _ext cc _upd_flag _args _body)) =
+ let live = stgLneLiveExpr cl
+ in Right <$> ((,,,) <$> identForId i
+ <*> varForEntryId i
+ <*> concatMapM varsForId live
+ <*> pure cc)
+
+-- fixme CgCase has a reps_compatible check here
+-- | Consume Stg case statement and generate a case statement. See also
+-- 'genAlts'
+genCase :: HasDebugCallStack
+ => ExprCtx
+ -> Id
+ -> CgStgExpr
+ -> AltType
+ -> [CgStgAlt]
+ -> LiveVars
+ -> G (JStat, ExprResult)
+genCase ctx bnd e at alts l
+ | snd (isInlineExpr (ctxEvaluatedIds ctx) e) = do
+ bndi <- identsForId bnd
+ let ctx' = ctxSetTop bnd
+ $ ctxSetTarget (assocIdExprs bnd (map toJExpr bndi))
+ $ ctx
+ (ej, r) <- genExpr ctx' e
+ let d = case r of
+ ExprInline d0 -> d0
+ ExprCont -> pprPanic "genCase: expression was not inline"
+ (pprStgExpr panicStgPprOpts e)
+
+ (aj, ar) <- genAlts (ctxAssertEvaluated bnd ctx) bnd at d alts
+ (saveCCS,restoreCCS) <- ifProfilingM $ do
+ ccsVar <- freshIdent
+ pure ( ccsVar ||= toJExpr jCurrentCCS
+ , toJExpr jCurrentCCS |= toJExpr ccsVar
+ )
+ return ( mconcat
+ [ mconcat (map decl bndi)
+ , saveCCS
+ , ej
+ , restoreCCS
+ , aj
+ ]
+ , ar
+ )
+ | otherwise = do
+ rj <- genRet (ctxAssertEvaluated bnd ctx) bnd at alts l
+ let ctx' = ctxSetTop bnd
+ $ ctxSetTarget (assocIdExprs bnd (map toJExpr [R1 ..]))
+ $ ctx
+ (ej, _r) <- genExpr ctx' e
+ return (rj <> ej, ExprCont)
+
+genRet :: HasDebugCallStack
+ => ExprCtx
+ -> Id
+ -> AltType
+ -> [CgStgAlt]
+ -> LiveVars
+ -> G JStat
+genRet ctx e at as l = freshIdent >>= f
+ where
+ allRefs :: [Id]
+ allRefs = S.toList . S.unions $ fmap (exprRefs emptyUFM . alt_rhs) as
+ lneLive :: Int
+ lneLive = maximum $ 0 : catMaybes (map (ctxLneBindingStackSize ctx) allRefs)
+ ctx' = ctxLneShrinkStack ctx lneLive
+ lneVars = map fst $ ctxLneFrameVars ctx'
+ isLne i = ctxIsLneBinding ctx i || ctxIsLneLiveVar ctx' i
+ nonLne = filter (not . isLne) (dVarSetElems l)
+
+ f :: Ident -> G JStat
+ f r@(TxtI ri) = do
+ pushLne <- pushLneFrame lneLive ctx
+ saveCCS <- ifProfilingM $ push [jCurrentCCS]
+ free <- optimizeFree 0 nonLne
+ pushRet <- pushRetArgs free (toJExpr r)
+ fun' <- fun free
+ sr <- genStaticRefs l -- srt
+ prof <- profiling
+ emitClosureInfo $
+ ClosureInfo r
+ (CIRegs 0 altRegs)
+ ri
+ (fixedLayout . reverse $
+ map (stackSlotType . fst3) free
+ ++ if prof then [ObjV] else map stackSlotType lneVars)
+ CIStackFrame
+ sr
+ emitToplevel $ r ||= toJExpr (JFunc [] fun')
+ return (pushLne <> saveCCS <> pushRet)
+ fst3 ~(x,_,_) = x
+
+ altRegs :: HasDebugCallStack => [VarType]
+ altRegs = case at of
+ PrimAlt ptc -> [primRepVt ptc]
+ MultiValAlt _n -> idVt e
+ _ -> [PtrV]
+
+ -- special case for popping CCS but preserving stack size
+ pop_handle_CCS :: [(JExpr, StackSlot)] -> G JStat
+ pop_handle_CCS [] = return mempty
+ pop_handle_CCS xs = do
+ -- grab the slots from 'xs' and push
+ addSlots (map snd xs)
+ -- move the stack pointer into the stack by ''length xs + n'
+ a <- adjSpN (length xs)
+ -- now load from the top of the stack
+ return (loadSkip 0 (map fst xs) <> a)
+
+ fun free = resetSlots $ do
+ decs <- declVarsForId e
+ load <- flip assignAll (map toJExpr [R1 ..]) . map toJExpr <$> identsForId e
+ loadv <- verifyRuntimeReps [e]
+ ras <- loadRetArgs free
+ rasv <- verifyRuntimeReps (map (\(x,_,_)->x) free)
+ restoreCCS <- ifProfilingM . pop_handle_CCS $ pure (jCurrentCCS, SlotUnknown)
+ rlne <- popLneFrame False lneLive ctx'
+ rlnev <- verifyRuntimeReps lneVars
+ (alts, _altr) <- genAlts ctx' e at Nothing as
+ return $ decs <> load <> loadv <> ras <> rasv <> restoreCCS <> rlne <> rlnev <> alts <>
+ returnStack
+
+-- | Consume an Stg case alternative and generate the corresponding alternative
+-- in JS land. If one alternative is a continuation then we must normalize the
+-- other alternatives. See 'Branch' and 'normalizeBranches'.
+genAlts :: HasDebugCallStack
+ => ExprCtx -- ^ lhs to assign expression result to
+ -> Id -- ^ id being matched
+ -> AltType -- ^ type
+ -> Maybe [JExpr] -- ^ if known, fields in datacon from earlier expression
+ -> [CgStgAlt] -- ^ the alternatives
+ -> G (JStat, ExprResult)
+genAlts ctx e at me alts = do
+ (st, er) <- case at of
+
+ PolyAlt -> case alts of
+ [alt] -> (branch_stat &&& branch_result) <$> mkAlgBranch ctx e alt
+ _ -> panic "genAlts: multiple polyalt"
+
+ PrimAlt _tc
+ | [GenStgAlt _ bs expr] <- alts
+ -> do
+ ie <- varsForId e
+ dids <- mconcat <$> mapM declVarsForId bs
+ bss <- concatMapM varsForId bs
+ (ej, er) <- genExpr ctx expr
+ return (dids <> assignAll bss ie <> ej, er)
+
+ PrimAlt tc
+ -> do
+ ie <- varsForId e
+ (r, bss) <- normalizeBranches ctx <$>
+ mapM (isolateSlots . mkPrimIfBranch ctx [primRepVt tc]) alts
+ setSlots []
+ return (mkSw ie bss, r)
+
+ MultiValAlt n
+ | [GenStgAlt _ bs expr] <- alts
+ -> do
+ eids <- varsForId e
+ l <- loadUbxTup eids bs n
+ (ej, er) <- genExpr ctx expr
+ return (l <> ej, er)
+
+ AlgAlt tc
+ | [_alt] <- alts
+ , isUnboxedTupleTyCon tc
+ -> panic "genAlts: unexpected unboxed tuple"
+
+ AlgAlt _tc
+ | Just es <- me
+ , [GenStgAlt (DataAlt dc) bs expr] <- alts
+ , not (isUnboxableCon dc)
+ -> do
+ bsi <- mapM identsForId bs
+ (ej, er) <- genExpr ctx expr
+ return (declAssignAll (concat bsi) es <> ej, er)
+
+ AlgAlt _tc
+ | [alt] <- alts
+ -> do
+ Branch _ s r <- mkAlgBranch ctx e alt
+ return (s, r)
+
+ AlgAlt _tc
+ | [alt,_] <- alts
+ , DataAlt dc <- alt_con alt
+ , isBoolDataCon dc
+ -> do
+ i <- varForId e
+ nbs <- normalizeBranches ctx <$>
+ mapM (isolateSlots . mkAlgBranch ctx e) alts
+ case nbs of
+ (r, [Branch _ s1 _, Branch _ s2 _]) -> do
+ let s = if dataConTag dc == 2
+ then IfStat i s1 s2
+ else IfStat i s2 s1
+ setSlots []
+ return (s, r)
+ _ -> error "genAlts: invalid branches for Bool"
+
+ AlgAlt _tc -> do
+ ei <- varForId e
+ (r, brs) <- normalizeBranches ctx <$>
+ mapM (isolateSlots . mkAlgBranch ctx e) alts
+ setSlots []
+ return (mkSwitch (ei .^ "f" .^ "a") brs, r)
+
+ _ -> pprPanic "genAlts: unhandled case variant" (ppr (at, length alts))
+
+ ver <- verifyMatchRep e at
+ pure (ver <> st, er)
+
+-- | If 'StgToJSConfig.csRuntimeAssert' is set, then generate an assertion that
+-- asserts the pattern match is valid, e.g., the match is attempted on a
+-- Boolean, a Data Constructor, or some number.
+verifyMatchRep :: HasDebugCallStack => Id -> AltType -> G JStat
+verifyMatchRep x alt = do
+ runtime_assert <- csRuntimeAssert <$> getSettings
+ if not runtime_assert
+ then pure mempty
+ else case alt of
+ AlgAlt tc -> do
+ ix <- varsForId x
+ pure $ ApplStat (var "h$verify_match_alg") (ValExpr(JStr(mkFastString (renderWithContext defaultSDocContext (ppr tc)))):ix)
+ _ -> pure mempty
+
+-- | A 'Branch' represents a possible branching path of an Stg case statement,
+-- i.e., a possible code path from an 'StgAlt'
+data Branch a = Branch
+ { branch_expr :: a
+ , branch_stat :: JStat
+ , branch_result :: ExprResult
+ }
+ deriving (Eq,Functor)
+
+-- | If one branch ends in a continuation but another is inline, we need to
+-- adjust the inline branch to use the continuation convention
+normalizeBranches :: ExprCtx
+ -> [Branch a]
+ -> (ExprResult, [Branch a])
+normalizeBranches ctx brs
+ | all (==ExprCont) (fmap branch_result brs) =
+ (ExprCont, brs)
+ | branchResult (fmap branch_result brs) == ExprCont =
+ (ExprCont, map mkCont brs)
+ | otherwise =
+ (ExprInline Nothing, brs)
+ where
+ mkCont b = case branch_result b of
+ ExprInline{} -> b { branch_stat = branch_stat b <> assignAll jsRegsFromR1
+ (concatMap typex_expr $ ctxTarget ctx)
+ , branch_result = ExprCont
+ }
+ _ -> b
+
+-- | Load an unboxed tuple. "Loading" means getting all 'Idents' from the input
+-- ID's, declaring them as variables in JS land and binding them, in order, to
+-- 'es'.
+loadUbxTup :: [JExpr] -> [Id] -> Int -> G JStat
+loadUbxTup es bs _n = do
+ bs' <- concatMapM identsForId bs
+ return $ declAssignAll bs' es
+
+mkSw :: [JExpr] -> [Branch (Maybe [JExpr])] -> JStat
+mkSw [e] cases = mkSwitch e (fmap (fmap (fmap head)) cases)
+mkSw es cases = mkIfElse es cases
+
+-- | Switch for pattern matching on constructors or prims
+mkSwitch :: JExpr -> [Branch (Maybe JExpr)] -> JStat
+mkSwitch e cases
+ | [Branch (Just c1) s1 _] <- n
+ , [Branch _ s2 _] <- d
+ = IfStat (InfixExpr StrictEqOp e c1) s1 s2
+
+ | [Branch (Just c1) s1 _, Branch _ s2 _] <- n
+ , null d
+ = IfStat (InfixExpr StrictEqOp e c1) s1 s2
+
+ | null d
+ = SwitchStat e (map addBreak (init n)) (branch_stat (last n))
+
+ | [Branch _ d0 _] <- d
+ = SwitchStat e (map addBreak n) d0
+
+ | otherwise = panic "mkSwitch: multiple default cases"
+ where
+ addBreak (Branch (Just c) s _) = (c, mconcat [s, BreakStat Nothing])
+ addBreak _ = panic "mkSwitch: addBreak"
+ (n,d) = L.partition (isJust . branch_expr) cases
+
+-- | if/else for pattern matching on things that js cannot switch on
+-- the list of branches is expected to have the default alternative
+-- first, if it exists
+mkIfElse :: [JExpr] -> [Branch (Maybe [JExpr])] -> JStat
+mkIfElse e s = go (L.reverse s)
+ where
+ go = \case
+ [Branch _ s _] -> s -- only one 'nothing' allowed
+ (Branch (Just e0) s _ : xs) -> IfStat (mkEq e e0) s (go xs)
+ [] -> panic "mkIfElse: empty expression list"
+ _ -> panic "mkIfElse: multiple DEFAULT cases"
+
+-- | Wrapper to contruct sequences of (===), e.g.,
+--
+-- > mkEq [l0,l1,l2] [r0,r1,r2] = (l0 === r0) && (l1 === r1) && (l2 === r2)
+--
+mkEq :: [JExpr] -> [JExpr] -> JExpr
+mkEq es1 es2
+ | length es1 == length es2 = foldl1 (InfixExpr LAndOp) (zipWith (InfixExpr StrictEqOp) es1 es2)
+ | otherwise = panic "mkEq: incompatible expressions"
+
+mkAlgBranch :: ExprCtx -- ^ toplevel id for the result
+ -> Id -- ^ datacon to match
+ -> CgStgAlt -- ^ match alternative with binders
+ -> G (Branch (Maybe JExpr))
+mkAlgBranch top d alt
+ | DataAlt dc <- alt_con alt
+ , isUnboxableCon dc
+ , [b] <- alt_bndrs alt
+ = do
+ idd <- varForId d
+ fldx <- identsForId b
+ case fldx of
+ [fld] -> do
+ (ej, er) <- genExpr top (alt_rhs alt)
+ return (Branch Nothing (mconcat [fld ||= idd, ej]) er)
+ _ -> panic "mkAlgBranch: invalid size"
+
+ | otherwise
+ = do
+ cc <- caseCond (alt_con alt)
+ idd <- varForId d
+ b <- loadParams idd (alt_bndrs alt)
+ (ej, er) <- genExpr top (alt_rhs alt)
+ return (Branch cc (b <> ej) er)
+
+-- | Generate a primitive If-expression
+mkPrimIfBranch :: ExprCtx
+ -> [VarType]
+ -> CgStgAlt
+ -> G (Branch (Maybe [JExpr]))
+mkPrimIfBranch top _vt alt =
+ (\ic (ej,er) -> Branch ic ej er) <$> ifCond (alt_con alt) <*> genExpr top (alt_rhs alt)
+
+-- fixme are bool things always checked correctly here?
+ifCond :: AltCon -> G (Maybe [JExpr])
+ifCond = \case
+ DataAlt da -> return $ Just [toJExpr (dataConTag da)]
+ LitAlt l -> Just <$> genLit l
+ DEFAULT -> return Nothing
+
+caseCond :: AltCon -> G (Maybe JExpr)
+caseCond = \case
+ DEFAULT -> return Nothing
+ DataAlt da -> return $ Just (toJExpr $ dataConTag da)
+ LitAlt l -> genLit l >>= \case
+ [e] -> pure (Just e)
+ es -> pprPanic "caseCond: expected single-variable literal" (ppr es)
+
+-- fixme use single tmp var for all branches
+-- | Load parameters from constructor
+loadParams :: JExpr -> [Id] -> G JStat
+loadParams from args = do
+ as <- concat <$> zipWithM (\a u -> map (,u) <$> identsForId a) args use
+ return $ case as of
+ [] -> mempty
+ [(x,u)] -> loadIfUsed (from .^ closureField1_) x u
+ [(x1,u1),(x2,u2)] -> mconcat
+ [ loadIfUsed (from .^ closureField1_) x1 u1
+ , loadIfUsed (from .^ closureField2_) x2 u2
+ ]
+ ((x,u):xs) -> mconcat
+ [ loadIfUsed (from .^ closureField1_) x u
+ , jVar (\d -> mconcat [ d |= from .^ closureField2_
+ , loadConVarsIfUsed d xs
+ ])
+ ]
+ where
+ use = repeat True -- fixme clean up
+ loadIfUsed fr tgt True = tgt ||= fr
+ loadIfUsed _ _ _ = mempty
+
+ loadConVarsIfUsed fr cs = mconcat $ zipWith f cs [(1::Int)..]
+ where f (x,u) n = loadIfUsed (SelExpr fr (TxtI (dataFieldName n))) x u
+
+-- | Determine if a branch will end in a continuation or not. If not the inline
+-- branch must be normalized. See 'normalizeBranches'
+-- NB. not a Monoid
+branchResult :: HasDebugCallStack => [ExprResult] -> ExprResult
+branchResult = \case
+ [] -> panic "branchResult: empty list"
+ [e] -> e
+ (ExprCont:_) -> ExprCont
+ (_:es)
+ | elem ExprCont es -> ExprCont
+ | otherwise -> ExprInline Nothing
+
+-- | Push return arguments onto the stack. The 'Bool' tracks whether the value
+-- is already on the stack or not, used in 'StgToJS.Stack.pushOptimized'.
+pushRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> JExpr -> G JStat
+pushRetArgs free fun = do
+ rs <- mapM (\(i,n,b) -> (\es->(es!!(n-1),b)) <$> genIdArg i) free
+ pushOptimized (rs++[(fun,False)])
+
+-- | Load the return arguments then pop the stack frame
+loadRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> G JStat
+loadRetArgs free = do
+ ids <- mapM (\(i,n,_b) -> (!! (n-1)) <$> genIdStackArgI i) free
+ popSkipI 1 ids
+
+-- | allocate multiple, possibly mutually recursive, closures
+allocDynAll :: Bool -> Maybe JStat -> [(Ident,JExpr,[JExpr],CostCentreStack)] -> G JStat
+{-
+XXX remove use of template and enable in-place init again
+allocDynAll haveDecl middle [(to,entry,free,cc)]
+ | isNothing middle && to `notElem` (free ^.. template) = do
+ ccs <- ccsVarJ cc
+ return $ allocDynamic s haveDecl to entry free ccs -}
+allocDynAll haveDecl middle cls = do
+ settings <- getSettings
+ let
+ middle' = fromMaybe mempty middle
+
+ decl_maybe i e
+ | haveDecl = toJExpr i |= e
+ | otherwise = i ||= e
+
+ makeObjs :: G JStat
+ makeObjs =
+ fmap mconcat $ forM cls $ \(i,f,_,cc) -> do
+ ccs <- maybeToList <$> costCentreStackLbl cc
+ pure $ mconcat
+ [ decl_maybe i $ if csInlineAlloc settings
+ then ValExpr (jhFromList $ [ (closureEntry_ , f)
+ , (closureField1_, null_)
+ , (closureField2_, null_)
+ , (closureMeta_ , zero_)
+ ]
+ ++ fmap (\cid -> ("cc", ValExpr (JVar cid))) ccs)
+ else ApplExpr (var "h$c") (f : fmap (ValExpr . JVar) ccs)
+ ]
+
+ fillObjs = mconcat $ map fillObj cls
+ fillObj (i,_,es,_)
+ | csInlineAlloc settings || length es > 24 =
+ case es of
+ [] -> mempty
+ [ex] -> toJExpr i .^ closureField1_ |= toJExpr ex
+ [e1,e2] -> mconcat
+ [ toJExpr i .^ closureField1_ |= toJExpr e1
+ , toJExpr i .^ closureField2_ |= toJExpr e2
+ ]
+ (ex:es) -> mconcat
+ [ toJExpr i .^ closureField1_ |= toJExpr ex
+ , toJExpr i .^ closureField2_ |= toJExpr (jhFromList (zip dataFieldNames es))
+ ]
+ | otherwise = case es of
+ [] -> mempty
+ [ex] -> toJExpr i .^ closureField1_ |= ex
+ [e1,e2] -> mconcat
+ [ toJExpr i .^ closureField1_ |= e1
+ , toJExpr i .^ closureField2_ |= e2
+ ]
+ (ex:es) -> mconcat
+ [ toJExpr i .^ closureField1_ |= ex
+ , toJExpr i .^ closureField2_ |= fillFun es
+ ]
+
+ fillFun [] = null_
+ fillFun es = ApplExpr (allocData (length es)) es
+
+ checkObjs | csAssertRts settings = mconcat $
+ map (\(i,_,_,_) -> ApplStat (ValExpr (JVar (TxtI "h$checkObj"))) [toJExpr i]) cls
+ | otherwise = mempty
+
+ objs <- makeObjs
+ pure $ mconcat [objs, middle', fillObjs, checkObjs]
+
+-- | Generate a primop. This function wraps around the real generator
+-- 'GHC.StgToJS.genPrim', handling the 'ExprCtx' and all arguments before
+-- generating the primop.
+genPrimOp :: ExprCtx -> PrimOp -> [StgArg] -> Type -> G (JStat, ExprResult)
+genPrimOp ctx op args t = do
+ as <- concatMapM genArg args
+ prof <- csProf <$> getSettings
+ bound <- csBoundsCheck <$> getSettings
+ -- fixme: should we preserve/check the primreps?
+ return $ case genPrim prof bound t op (concatMap typex_expr $ ctxTarget ctx) as of
+ PrimInline s -> (s, ExprInline Nothing)
+ PRPrimCall s -> (s, ExprCont)
diff --git a/compiler/GHC/StgToJS/ExprCtx.hs b/compiler/GHC/StgToJS/ExprCtx.hs
new file mode 100644
index 0000000000..48a4483009
--- /dev/null
+++ b/compiler/GHC/StgToJS/ExprCtx.hs
@@ -0,0 +1,172 @@
+{-# LANGUAGE TupleSections #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.ExprCtx
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- TODO: Write my description!
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.ExprCtx
+ ( ExprCtx
+ , initExprCtx
+ , ctxAssertEvaluated
+ , ctxIsEvaluated
+ , ctxSetSrcSpan
+ , ctxSrcSpan
+ , ctxSetTop
+ , ctxTarget
+ , ctxSetTarget
+ , ctxEvaluatedIds
+ -- * Let-no-escape
+ , ctxClearLneFrame
+ , ctxUpdateLneFrame
+ , ctxLneFrameVars
+ , ctxLneFrameSize
+ , ctxIsLneBinding
+ , ctxIsLneLiveVar
+ , ctxLneBindingStackSize
+ , ctxLneShrinkStack
+ )
+where
+
+import GHC.Prelude
+
+import GHC.StgToJS.Types
+
+import GHC.Types.Unique.FM
+import GHC.Types.Unique.Set
+import GHC.Types.Var
+import GHC.Types.SrcLoc
+
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+
+import Data.Maybe
+
+
+-- | Context into which an expression is evaluated
+data ExprCtx = ExprCtx
+ { ctxTop :: Id
+ -- ^ Top-level binding Id
+
+ , ctxTarget :: [TypedExpr]
+ -- ^ Target variables for the evaluated expression
+
+ , ctxEvaluatedIds :: UniqSet Id
+ -- ^ Ids that we know to be evaluated (e.g. case binders when the expression
+ -- to evaluate is in an alternative)
+
+ , ctxSrcSpan :: Maybe RealSrcSpan
+ -- ^ Source location
+
+ ----------------------------
+ -- Handling of let-no-escape
+
+ , ctxLneFrameBs :: UniqFM Id Int
+ -- ^ LNE bindings with their expected stack size.
+ --
+ -- The Int is the size of the stack when the LNE binding was defined.
+ -- We need to shrink the stack back to this size when we enter one of the
+ -- associated binder rhs: it expects its free variables at certain offsets
+ -- in the stack.
+
+ , ctxLneFrameVars :: [(Id,Int)]
+ -- ^ Contents of current LNE frame
+ --
+ -- Variables and their index on the stack
+
+ , ctxLneFrameSize :: {-# UNPACK #-} !Int
+ -- ^ Cache the length of `ctxLneFrameVars`
+
+ }
+
+-- | Initialize an expression context in the context of the given top-level
+-- binding Id
+initExprCtx :: Id -> ExprCtx
+initExprCtx i = ExprCtx
+ { ctxTop = i
+ , ctxTarget = []
+ , ctxEvaluatedIds = emptyUniqSet
+ , ctxLneFrameBs = emptyUFM
+ , ctxLneFrameVars = []
+ , ctxLneFrameSize = 0
+ , ctxSrcSpan = Nothing
+ }
+
+-- | Set target
+ctxSetTarget :: [TypedExpr] -> ExprCtx -> ExprCtx
+ctxSetTarget t ctx = ctx { ctxTarget = t }
+
+-- | Set top-level binding Id
+ctxSetTop :: Id -> ExprCtx -> ExprCtx
+ctxSetTop i ctx = ctx { ctxTop = i }
+
+-- | Add an Id to the known-evaluated set
+ctxAssertEvaluated :: Id -> ExprCtx -> ExprCtx
+ctxAssertEvaluated i ctx = ctx { ctxEvaluatedIds = addOneToUniqSet (ctxEvaluatedIds ctx) i }
+
+-- | Set source location
+ctxSetSrcSpan :: RealSrcSpan -> ExprCtx -> ExprCtx
+ctxSetSrcSpan span ctx = ctx { ctxSrcSpan = Just span }
+
+-- | Update let-no-escape frame
+ctxUpdateLneFrame :: [(Id,Int)] -> [Id] -> ExprCtx -> ExprCtx
+ctxUpdateLneFrame new_spilled_vars new_lne_ids ctx =
+ let old_frame_size = ctxLneFrameSize ctx
+ new_frame_size = old_frame_size + length new_spilled_vars
+ in ctx
+ { ctxLneFrameBs = addListToUFM (ctxLneFrameBs ctx) (map (,new_frame_size) new_lne_ids)
+ , ctxLneFrameSize = new_frame_size
+ , ctxLneFrameVars = ctxLneFrameVars ctx ++ new_spilled_vars
+ }
+
+-- | Remove information about the current LNE frame
+ctxClearLneFrame :: ExprCtx -> ExprCtx
+ctxClearLneFrame ctx =
+ ctx
+ { ctxLneFrameBs = emptyUFM
+ , ctxLneFrameVars = []
+ , ctxLneFrameSize = 0
+ }
+
+-- | Predicate: do we know for sure that the given Id is evaluated?
+ctxIsEvaluated :: ExprCtx -> Id -> Bool
+ctxIsEvaluated ctx i = i `elementOfUniqSet` ctxEvaluatedIds ctx
+
+-- | Does the given Id correspond to a LNE binding
+ctxIsLneBinding :: ExprCtx -> Id -> Bool
+ctxIsLneBinding ctx i = isJust (ctxLneBindingStackSize ctx i)
+
+-- | Does the given Id correspond to a LNE live var on the stack
+ctxIsLneLiveVar :: ExprCtx -> Id -> Bool
+ctxIsLneLiveVar ctx i = i `elem` map fst (ctxLneFrameVars ctx)
+
+-- | Return the LNE stack size associated to the given Id.
+-- Return Nothing when the Id doesn't correspond to a LNE binding.
+ctxLneBindingStackSize :: ExprCtx -> Id -> Maybe Int
+ctxLneBindingStackSize ctx i = lookupUFM (ctxLneFrameBs ctx) i
+
+-- | Shrink the LNE stack to the given size
+ctxLneShrinkStack :: ExprCtx -> Int -> ExprCtx
+ctxLneShrinkStack ctx n =
+ let l = ctxLneFrameSize ctx
+ in assertPpr
+ (l >= n)
+ (vcat [ text "ctxLneShrinkStack: let-no-escape stack too short:"
+ , ppr l
+ , text " < "
+ , ppr n
+ ])
+ (ctx { ctxLneFrameVars = take n (ctxLneFrameVars ctx)
+ , ctxLneFrameSize = n
+ }
+ )
diff --git a/compiler/GHC/StgToJS/FFI.hs b/compiler/GHC/StgToJS/FFI.hs
new file mode 100644
index 0000000000..0c1a713f70
--- /dev/null
+++ b/compiler/GHC/StgToJS/FFI.hs
@@ -0,0 +1,352 @@
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.FFI
+ ( genPrimCall
+ , genForeignCall
+ , saturateFFI
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+import GHC.JS.Transform
+
+import GHC.StgToJS.Arg
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Types
+import GHC.StgToJS.Literal
+import GHC.StgToJS.Regs
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Ids
+
+import GHC.Types.RepType
+import GHC.Types.ForeignCall
+import GHC.Types.Unique.Map
+import GHC.Types.Unique.FM
+
+import GHC.Stg.Syntax
+
+import GHC.Builtin.PrimOps
+import GHC.Builtin.Types.Prim
+
+import GHC.Core.Type hiding (typeSize)
+
+import GHC.Utils.Misc
+import GHC.Utils.Panic
+import GHC.Utils.Outputable (renderWithContext, defaultSDocContext, ppr, vcat, text)
+import GHC.Data.FastString
+
+import Data.Char
+import Data.Monoid
+import Data.Maybe
+import qualified Data.List as L
+import Control.Monad
+import Control.Applicative
+import qualified Text.ParserCombinators.ReadP as P
+
+genPrimCall :: ExprCtx -> PrimCall -> [StgArg] -> Type -> G (JStat, ExprResult)
+genPrimCall ctx (PrimCall lbl _) args t = do
+ j <- parseFFIPattern False False False ("h$" ++ unpackFS lbl) t (concatMap typex_expr $ ctxTarget ctx) args
+ return (j, ExprInline Nothing)
+
+-- | generate the actual call
+{-
+ parse FFI patterns:
+ "&value -> value
+ 1. "function" -> ret = function(...)
+ 2. "$r = $1.f($2) -> r1 = a1.f(a2)
+
+ arguments, $1, $2, $3 unary arguments
+ $1_1, $1_2, for a binary argument
+
+ return type examples
+ 1. $r unary return
+ 2. $r1, $r2 binary return
+ 3. $r1, $r2, $r3_1, $r3_2 unboxed tuple return
+ -}
+parseFFIPattern :: Bool -- ^ catch exception and convert them to haskell exceptions
+ -> Bool -- ^ async (only valid with javascript calling conv)
+ -> Bool -- ^ using javascript calling convention
+ -> String
+ -> Type
+ -> [JExpr]
+ -> [StgArg]
+ -> G JStat
+parseFFIPattern catchExcep async jscc pat t es as
+ | catchExcep = do
+ c <- parseFFIPatternA async jscc pat t es as
+ -- Generate:
+ -- try {
+ -- `c`;
+ -- } catch(except) {
+ -- return h$throwJSException(except);
+ -- }
+ let ex = TxtI "except"
+ return (TryStat c ex (ReturnStat (ApplExpr (var "h$throwJSException") [toJExpr ex])) mempty)
+ | otherwise = parseFFIPatternA async jscc pat t es as
+
+parseFFIPatternA :: Bool -- ^ async
+ -> Bool -- ^ using JavaScript calling conv
+ -> String
+ -> Type
+ -> [JExpr]
+ -> [StgArg]
+ -> G JStat
+-- async calls get an extra callback argument
+-- call it with the result
+parseFFIPatternA True True pat t es as = do
+ cb <- freshIdent
+ x <- freshIdent
+ d <- freshIdent
+ stat <- parseFFIPattern' (Just (toJExpr cb)) True pat t es as
+ return $ mconcat
+ [ x ||= (toJExpr (jhFromList [("mv", null_)]))
+ , cb ||= ApplExpr (var "h$mkForeignCallback") [toJExpr x]
+ , stat
+ , IfStat (InfixExpr StrictEqOp (toJExpr x .^ "mv") null_)
+ (mconcat
+ [ toJExpr x .^ "mv" |= UOpExpr NewOp (ApplExpr (var "h$MVar") [])
+ , sp |= Add sp one_
+ , (IdxExpr stack sp) |= var "h$unboxFFIResult"
+ , ReturnStat $ ApplExpr (var "h$takeMVar") [toJExpr x .^ "mv"]
+ ])
+ (mconcat
+ [ d ||= toJExpr x .^ "mv"
+ , copyResult (toJExpr d)
+ ])
+ ]
+ where nrst = typeSize t
+ copyResult d = assignAllEqual es (map (IdxExpr d . toJExpr) [0..nrst-1])
+parseFFIPatternA _async javascriptCc pat t es as =
+ parseFFIPattern' Nothing javascriptCc pat t es as
+
+-- parseFFIPatternA _ _ _ _ _ _ = error "parseFFIPattern: non-JavaScript pattern must be synchronous"
+
+parseFFIPattern' :: Maybe JExpr -- ^ Nothing for sync, Just callback for async
+ -> Bool -- ^ javascript calling convention used
+ -> String -- ^ pattern called
+ -> Type -- ^ return type
+ -> [JExpr] -- ^ expressions to return in (may be more than necessary)
+ -> [StgArg] -- ^ arguments
+ -> G JStat
+parseFFIPattern' callback javascriptCc pat t ret args
+ | not javascriptCc = mkApply pat
+ | otherwise =
+ if True
+ then mkApply pat
+ else do
+ u <- freshUnique
+ case parseFfiJME pat u of
+ Right (ValExpr (JVar (TxtI _ident))) -> mkApply pat
+ Right expr | not async && length tgt < 2 -> do
+ (statPre, ap) <- argPlaceholders javascriptCc args
+ let rp = resultPlaceholders async t ret
+ env = addListToUFM emptyUFM (rp ++ ap)
+ if length tgt == 1
+ then return $ statPre <> (mapStatIdent (replaceIdent env) (var "$r" |= expr))
+ else return $ statPre <> (mapStatIdent (replaceIdent env) (toStat expr))
+ Right _ -> p $ "invalid expression FFI pattern. Expression FFI patterns can only be used for synchronous FFI " ++
+ " imports with result size 0 or 1.\n" ++ pat
+ Left _ -> case parseFfiJM pat u of
+ Left err -> p (show err)
+ Right stat -> do
+ let rp = resultPlaceholders async t ret
+ let cp = callbackPlaceholders callback
+ (statPre, ap) <- argPlaceholders javascriptCc args
+ let env = addListToUFM emptyUFM (rp ++ ap ++ cp)
+ return $ statPre <> (mapStatIdent (replaceIdent env) stat) -- fixme trace?
+ where
+ async = isJust callback
+ tgt = take (typeSize t) ret
+ -- automatic apply, build call and result copy
+ mkApply f
+ | Just cb <- callback = do
+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
+ cs <- getSettings
+ return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as++[cb])
+ | {-ts@-}
+ (t:ts') <- tgt = do
+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
+ cs <- getSettings
+ return $ traceCall cs as
+ <> mconcat stats
+ <> (t |= ApplExpr f' (concat as) )
+ <> copyResult ts'
+ -- _ -> error "mkApply: empty list"
+ | otherwise = do
+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
+ cs <- getSettings
+ return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as)
+ where f' = toJExpr (TxtI $ mkFastString f)
+ copyResult rs = mconcat $ zipWith (\t r -> toJExpr r |= toJExpr t) (enumFrom Ret1) rs
+ p e = error ("Parse error in FFI pattern: " ++ pat ++ "\n" ++ e)
+
+ replaceIdent :: UniqFM Ident JExpr -> Ident -> JExpr
+ replaceIdent env i
+ | isFFIPlaceholder i = fromMaybe err (lookupUFM env i)
+ | otherwise = ValExpr (JVar i)
+ where
+ (TxtI i') = i
+ err = pprPanic "parseFFIPattern': invalid placeholder, check function type"
+ (vcat [text pat, ppr i', ppr args, ppr t])
+ traceCall cs as
+ | csTraceForeign cs = ApplStat (var "h$traceForeign") [toJExpr pat, toJExpr as]
+ | otherwise = mempty
+
+-- ident is $N, $N_R, $rN, $rN_R or $r or $c
+isFFIPlaceholder :: Ident -> Bool
+isFFIPlaceholder (TxtI x) = not (null (P.readP_to_S parser (unpackFS x)))
+ where
+ digit = P.satisfy (`elem` ("0123456789" :: String))
+ parser = void (P.string "$r" >> P.eof) <|>
+ void (P.string "$c" >> P.eof) <|> do
+ _ <- P.char '$'
+ P.optional (P.char 'r')
+ _ <- P.many1 digit
+ P.optional (P.char '_' >> P.many1 digit)
+ P.eof
+
+-- generate arg to be passed to FFI call, with marshalling JStat to be run
+-- before the call
+genFFIArg :: Bool -> StgArg -> G (JStat, [JExpr])
+genFFIArg _isJavaScriptCc (StgLitArg l) = (mempty,) <$> genLit l
+genFFIArg isJavaScriptCc a@(StgVarArg i)
+ | not isJavaScriptCc &&
+ (tycon == byteArrayPrimTyCon || tycon == mutableByteArrayPrimTyCon) =
+ (\x -> (mempty,[x, zero_])) <$> varForId i
+ | isVoid r = return (mempty, [])
+-- | Just x <- marshalFFIArg a = x
+ | isMultiVar r = (mempty,) <$> mapM (varForIdN i) [1..varSize r]
+ | otherwise = (\x -> (mempty,[x])) <$> varForId i
+ where
+ tycon = tyConAppTyCon (unwrapType arg_ty)
+ arg_ty = stgArgType a
+ r = uTypeVt arg_ty
+
+-- $1, $2, $3 for single, $1_1, $1_2 etc for dual
+-- void args not counted
+argPlaceholders :: Bool -> [StgArg] -> G (JStat, [(Ident,JExpr)])
+argPlaceholders isJavaScriptCc args = do
+ (stats, idents0) <- unzip <$> mapM (genFFIArg isJavaScriptCc) args
+ let idents = filter (not . null) idents0
+ return $ (mconcat stats, concat
+ (zipWith (\is n -> mkPlaceholder True ("$"++show n) is) idents [(1::Int)..]))
+
+mkPlaceholder :: Bool -> String -> [JExpr] -> [(Ident, JExpr)]
+mkPlaceholder undersc prefix aids =
+ case aids of
+ [] -> []
+ [x] -> [(TxtI . mkFastString $ prefix, x)]
+ xs@(x:_) -> (TxtI . mkFastString $ prefix, x) :
+ zipWith (\x m -> (TxtI . mkFastString $ prefix ++ u ++ show m,x)) xs [(1::Int)..]
+ where u = if undersc then "_" else ""
+
+-- $r for single, $r1,$r2 for dual
+-- $r1, $r2, etc for ubx tup, void args not counted
+resultPlaceholders :: Bool -> Type -> [JExpr] -> [(Ident,JExpr)] -- ident, replacement
+resultPlaceholders True _ _ = [] -- async has no direct resuls, use callback
+resultPlaceholders False t rs =
+ case typeVt (unwrapType t) of
+ [t'] -> mkUnary (varSize t')
+ uts ->
+ let sizes = filter (>0) (map varSize uts)
+ f _ 0 = []
+ f n 1 = [["$r" ++ show n]]
+ f n k = ["$r" ++ sn, "$r" ++ sn ++ "_1"] : map (\x -> ["$r" ++ sn ++ "_" ++ show x]) [2..k]
+ where sn = show n
+ phs = zipWith (\size n -> f n size) sizes [(1::Int)..]
+ in case sizes of
+ [n] -> mkUnary n
+ _ -> concat $ zipWith (\phs' r -> map (\i -> (TxtI (mkFastString i), r)) phs') (concat phs) rs
+ where
+ mkUnary 0 = []
+ mkUnary 1 = [(TxtI "$r",head rs)] -- single
+ mkUnary n = [(TxtI "$r",head rs),(TxtI "$r1", head rs)] ++
+ zipWith (\n r -> (TxtI . mkFastString $ "$r" ++ show n, toJExpr r)) [2..n] (tail rs)
+
+callbackPlaceholders :: Maybe JExpr -> [(Ident,JExpr)]
+callbackPlaceholders Nothing = []
+callbackPlaceholders (Just e) = [((TxtI "$c"), e)]
+
+parseFfiJME :: String -> Int -> Either String JExpr
+parseFfiJME _xs _u = Left "parseFfiJME not yet implemented"
+
+parseFfiJM :: String -> Int -> Either String JStat
+parseFfiJM _xs _u = Left "parseFfiJM not yet implemented"
+
+saturateFFI :: JMacro a => Int -> a -> a
+saturateFFI u = jsSaturate (Just . mkFastString $ "ghcjs_ffi_sat_" ++ show u)
+
+genForeignCall :: HasDebugCallStack
+ => ExprCtx
+ -> ForeignCall
+ -> Type
+ -> [JExpr]
+ -> [StgArg]
+ -> G (JStat, ExprResult)
+genForeignCall _ctx
+ (CCall (CCallSpec (StaticTarget _ tgt Nothing True)
+ JavaScriptCallConv
+ PlayRisky))
+ _t
+ [obj]
+ args
+ | tgt == fsLit "h$buildObject"
+ , Just pairs <- getObjectKeyValuePairs args = do
+ pairs' <- mapM (\(k,v) -> genArg v >>= \vs -> return (k, head vs)) pairs
+ return ( (|=) obj (ValExpr (JHash $ listToUniqMap pairs'))
+ , ExprInline Nothing
+ )
+
+genForeignCall ctx (CCall (CCallSpec ccTarget cconv safety)) t tgt args = do
+ emitForeign (ctxSrcSpan ctx) (mkFastString lbl) safety cconv (map showArgType args) (showType t)
+ (,exprResult) <$> parseFFIPattern catchExcep async isJsCc lbl t tgt' args
+ where
+ isJsCc = cconv == JavaScriptCallConv
+
+ lbl | (StaticTarget _ clbl _mpkg _isFunPtr) <- ccTarget
+ = let clbl' = unpackFS clbl
+ in if | isJsCc -> clbl'
+ | wrapperPrefix `L.isPrefixOf` clbl' ->
+ ("h$" ++ (drop 2 $ dropWhile isDigit $ drop (length wrapperPrefix) clbl'))
+ | otherwise -> "h$" ++ clbl'
+ | otherwise = "h$callDynamic"
+
+ exprResult | async = ExprCont
+ | otherwise = ExprInline Nothing
+
+ catchExcep = (cconv == JavaScriptCallConv) &&
+ playSafe safety || playInterruptible safety
+
+ async | isJsCc = playInterruptible safety
+ | otherwise = playInterruptible safety || playSafe safety
+
+ tgt' | async = take (length tgt) jsRegsFromR1
+ | otherwise = tgt
+
+ wrapperPrefix = "ghczuwrapperZC"
+
+getObjectKeyValuePairs :: [StgArg] -> Maybe [(FastString, StgArg)]
+getObjectKeyValuePairs [] = Just []
+getObjectKeyValuePairs (k:v:xs)
+ | Just t <- argJSStringLitUnfolding k =
+ fmap ((t,v):) (getObjectKeyValuePairs xs)
+getObjectKeyValuePairs _ = Nothing
+
+argJSStringLitUnfolding :: StgArg -> Maybe FastString
+argJSStringLitUnfolding (StgVarArg _v) = Nothing -- fixme
+argJSStringLitUnfolding _ = Nothing
+
+showArgType :: StgArg -> FastString
+showArgType a = showType (stgArgType a)
+
+showType :: Type -> FastString
+showType t
+ | Just tc <- tyConAppTyCon_maybe (unwrapType t) =
+ mkFastString (renderWithContext defaultSDocContext (ppr tc))
+ | otherwise = "<unknown>"
diff --git a/compiler/GHC/StgToJS/Heap.hs b/compiler/GHC/StgToJS/Heap.hs
new file mode 100644
index 0000000000..fe2955812d
--- /dev/null
+++ b/compiler/GHC/StgToJS/Heap.hs
@@ -0,0 +1,155 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+module GHC.StgToJS.Heap
+ ( closureType
+ , entryClosureType
+ , isObject
+ , isThunk
+ , isThunk'
+ , isBlackhole
+ , isFun
+ , isFun'
+ , isPap
+ , isPap'
+ , isCon
+ , isCon'
+ , conTag
+ , conTag'
+ , closureEntry
+ , closureMeta
+ , closureField1
+ , closureField2
+ , closureCC
+ , funArity
+ , funArity'
+ , papArity
+ , funOrPapArity
+ -- * Field names
+ , closureEntry_
+ , closureMeta_
+ , closureCC_
+ , closureField1_
+ , closureField2_
+ -- * Javascript Type literals
+ , jTyObject
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+import GHC.StgToJS.Types
+import GHC.Data.FastString
+
+closureEntry_ :: FastString
+closureEntry_ = "f"
+
+closureField1_ :: FastString
+closureField1_ = "d1"
+
+closureField2_ :: FastString
+closureField2_ = "d2"
+
+closureMeta_ :: FastString
+closureMeta_ = "m"
+
+closureCC_ :: FastString
+closureCC_ = "cc"
+
+entryClosureType_ :: FastString
+entryClosureType_ = "t"
+
+entryConTag_ :: FastString
+entryConTag_ = "a"
+
+entryFunArity_ :: FastString
+entryFunArity_ = "a"
+
+jTyObject :: JExpr
+jTyObject = jString "object"
+
+closureType :: JExpr -> JExpr
+closureType = entryClosureType . closureEntry
+
+entryClosureType :: JExpr -> JExpr
+entryClosureType f = f .^ entryClosureType_
+
+isObject :: JExpr -> JExpr
+isObject c = typeof c .===. String "object"
+
+isThunk :: JExpr -> JExpr
+isThunk c = closureType c .===. toJExpr Thunk
+
+isThunk' :: JExpr -> JExpr
+isThunk' f = entryClosureType f .===. toJExpr Thunk
+
+isBlackhole :: JExpr -> JExpr
+isBlackhole c = closureType c .===. toJExpr Blackhole
+
+isFun :: JExpr -> JExpr
+isFun c = closureType c .===. toJExpr Fun
+
+isFun' :: JExpr -> JExpr
+isFun' f = entryClosureType f .===. toJExpr Fun
+
+isPap :: JExpr -> JExpr
+isPap c = closureType c .===. toJExpr Pap
+
+isPap' :: JExpr -> JExpr
+isPap' f = entryClosureType f .===. toJExpr Pap
+
+isCon :: JExpr -> JExpr
+isCon c = closureType c .===. toJExpr Con
+
+isCon' :: JExpr -> JExpr
+isCon' f = entryClosureType f .===. toJExpr Con
+
+conTag :: JExpr -> JExpr
+conTag = conTag' . closureEntry
+
+conTag' :: JExpr -> JExpr
+conTag' f = f .^ entryConTag_
+
+-- | Get closure entry function
+closureEntry :: JExpr -> JExpr
+closureEntry p = p .^ closureEntry_
+
+-- | Get closure metadata
+closureMeta :: JExpr -> JExpr
+closureMeta p = p .^ closureMeta_
+
+-- | Get closure cost-center
+closureCC :: JExpr -> JExpr
+closureCC p = p .^ closureCC_
+
+-- | Get closure extra field 1
+closureField1 :: JExpr -> JExpr
+closureField1 p = p .^ closureField1_
+
+-- | Get closure extra field 2
+closureField2 :: JExpr -> JExpr
+closureField2 p = p .^ closureField2_
+
+-- number of arguments (arity & 0xff = arguments, arity >> 8 = number of registers)
+funArity :: JExpr -> JExpr
+funArity = funArity' . closureEntry
+
+-- function arity with raw reference to the entry
+funArity' :: JExpr -> JExpr
+funArity' f = f .^ entryFunArity_
+
+-- arity of a partial application
+papArity :: JExpr -> JExpr
+papArity cp = closureField1 (closureField2 cp)
+
+funOrPapArity
+ :: JExpr -- ^ heap object
+ -> Maybe JExpr -- ^ reference to entry, if you have one already (saves a c.f lookup twice)
+ -> JExpr -- ^ arity tag (tag >> 8 = registers, tag & 0xff = arguments)
+funOrPapArity c = \case
+ Nothing -> ((IfExpr (toJExpr (isFun c))) (toJExpr (funArity c)))
+ (toJExpr (papArity c))
+ Just f -> ((IfExpr (toJExpr (isFun' f))) (toJExpr (funArity' f)))
+ (toJExpr (papArity c))
diff --git a/compiler/GHC/StgToJS/Ids.hs b/compiler/GHC/StgToJS/Ids.hs
new file mode 100644
index 0000000000..5d28b511f6
--- /dev/null
+++ b/compiler/GHC/StgToJS/Ids.hs
@@ -0,0 +1,238 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Ids
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Module to deal with JS identifiers
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Ids
+ ( freshUnique
+ , freshIdent
+ , makeIdentForId
+ , cachedIdentForId
+ -- * Helpers for Idents
+ , identForId
+ , identForIdN
+ , identsForId
+ , identForEntryId
+ , identForDataConEntryId
+ , identForDataConWorker
+ -- * Helpers for variables
+ , varForId
+ , varForIdN
+ , varsForId
+ , varForEntryId
+ , varForDataConEntryId
+ , varForDataConWorker
+ , declVarsForId
+ )
+where
+
+import GHC.Prelude
+
+import GHC.StgToJS.Types
+import GHC.StgToJS.Monad
+import GHC.StgToJS.CoreUtils
+import GHC.StgToJS.Symbols
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.Core.DataCon
+import GHC.Types.Id
+import GHC.Types.Unique
+import GHC.Types.Unique.FM
+import GHC.Types.Name
+import GHC.Unit.Module
+import GHC.Data.FastString
+import GHC.Data.FastMutInt
+
+import Control.Monad
+import Control.Monad.IO.Class
+import qualified Control.Monad.Trans.State.Strict as State
+import qualified Data.Map as M
+import Data.Maybe
+import qualified Data.ByteString.Char8 as BSC
+
+-- | Get fresh unique number
+freshUnique :: G Int
+freshUnique = do
+ id_gen <- State.gets gsId
+ liftIO $ do
+ -- no need for atomicFetchAdd as we don't use threads in G
+ v <- readFastMutInt id_gen
+ writeFastMutInt id_gen (v+1)
+ pure v
+
+-- | Get fresh local Ident of the form: h$$unit:module_uniq
+freshIdent :: G Ident
+freshIdent = do
+ i <- freshUnique
+ mod <- State.gets gsModule
+ let !name = mkFreshJsSymbol mod i
+ return (TxtI name)
+
+
+-- | Generate unique Ident for the given ID (uncached!)
+--
+-- The ident has the following forms:
+--
+-- global Id: h$unit:module.name[_num][_type_suffix]
+-- local Id: h$$unit:module.name[_num][_type_suffix]_uniq
+--
+-- Note that the string is z-encoded except for "_" delimiters.
+--
+-- Optional "_type_suffix" can be:
+-- - "_e" for IdEntry
+-- - "_con_e" for IdConEntry
+--
+-- Optional "_num" is passed as an argument to this function. It is used for
+-- Haskell Ids that require several JS variables: e.g. 64-bit numbers (Word64#,
+-- Int64#), Addr#, StablePtr#, unboxed tuples, etc.
+--
+makeIdentForId :: Id -> Maybe Int -> IdType -> Module -> Ident
+makeIdentForId i num id_type current_module = TxtI ident
+ where
+ exported = isExportedId i
+ name = getName i
+ mod
+ | exported
+ , Just m <- nameModule_maybe name
+ = m
+ | otherwise
+ = current_module
+
+ !ident = mkFastStringByteString $ mconcat
+ [ mkJsSymbolBS exported mod (occNameFS (nameOccName name))
+
+ -------------
+ -- suffixes
+
+ -- suffix for Ids represented with more than one JS var ("_0", "_1", etc.)
+ , case num of
+ Nothing -> mempty
+ Just v -> mconcat [BSC.pack "_", intBS v]
+
+ -- suffix for entry and constructor entry
+ , case id_type of
+ IdPlain -> mempty
+ IdEntry -> BSC.pack "_e"
+ IdConEntry -> BSC.pack "_con_e"
+
+ -- unique suffix for non-exported Ids
+ , if exported
+ then mempty
+ else let (c,u) = unpkUnique (getUnique i)
+ in mconcat [BSC.pack ['_',c,'_'], intBS u]
+ ]
+
+-- | Retrieve the cached Ident for the given Id if there is one. Otherwise make
+-- a new one with 'makeIdentForId' and cache it.
+cachedIdentForId :: Id -> Maybe Int -> IdType -> G Ident
+cachedIdentForId i mi id_type = do
+
+ -- compute key
+ let !key = IdKey (getKey . getUnique $ i) (fromMaybe 0 mi) id_type
+
+ -- lookup Ident in the Ident cache
+ IdCache cache <- State.gets gsIdents
+ ident <- case M.lookup key cache of
+ Just ident -> pure ident
+ Nothing -> do
+ mod <- State.gets gsModule
+ let !ident = makeIdentForId i mi id_type mod
+ let !cache' = IdCache (M.insert key ident cache)
+ State.modify (\s -> s { gsIdents = cache' })
+ pure ident
+
+ -- Now update the GlobalId cache, if required
+
+ let update_global_cache = isGlobalId i && isNothing mi && id_type == IdPlain
+ -- fixme also allow caching entries for lifting?
+
+ when (update_global_cache) $ do
+ GlobalIdCache gidc <- getGlobalIdCache
+ case elemUFM ident gidc of
+ False -> setGlobalIdCache $ GlobalIdCache (addToUFM gidc ident (key, i))
+ True -> pure ()
+
+ pure ident
+
+-- | Retrieve default Ident for the given Id
+identForId :: Id -> G Ident
+identForId i = cachedIdentForId i Nothing IdPlain
+
+-- | Retrieve default Ident for the given Id with sub index
+--
+-- Some types, Word64, Addr#, unboxed tuple have more than one corresponding JS
+-- var, hence we use the sub index to identify each subpart / JS variable.
+identForIdN :: Id -> Int -> G Ident
+identForIdN i n = cachedIdentForId i (Just n) IdPlain
+
+-- | Retrieve all the idents for the given Id.
+identsForId :: Id -> G [Ident]
+identsForId i = case typeSize (idType i) of
+ 0 -> pure mempty
+ 1 -> (:[]) <$> identForId i
+ s -> mapM (identForIdN i) [1..s]
+
+
+-- | Retrieve entry Ident for the given Id
+identForEntryId :: Id -> G Ident
+identForEntryId i = cachedIdentForId i Nothing IdEntry
+
+-- | Retrieve datacon entry Ident for the given Id
+--
+-- Different name than the datacon wrapper.
+identForDataConEntryId :: Id -> G Ident
+identForDataConEntryId i = cachedIdentForId i Nothing IdConEntry
+
+
+-- | Retrieve default variable name for the given Id
+varForId :: Id -> G JExpr
+varForId i = toJExpr <$> identForId i
+
+-- | Retrieve default variable name for the given Id with sub index
+varForIdN :: Id -> Int -> G JExpr
+varForIdN i n = toJExpr <$> identForIdN i n
+
+-- | Retrieve all the JS vars for the given Id
+varsForId :: Id -> G [JExpr]
+varsForId i = case typeSize (idType i) of
+ 0 -> pure mempty
+ 1 -> (:[]) <$> varForId i
+ s -> mapM (varForIdN i) [1..s]
+
+
+-- | Retrieve entry variable name for the given Id
+varForEntryId :: Id -> G JExpr
+varForEntryId i = toJExpr <$> identForEntryId i
+
+-- | Retrieve datacon entry variable name for the given Id
+varForDataConEntryId :: Id -> G JExpr
+varForDataConEntryId i = ValExpr . JVar <$> identForDataConEntryId i
+
+
+-- | Retrieve datacon worker entry variable name for the given datacon
+identForDataConWorker :: DataCon -> G Ident
+identForDataConWorker d = identForDataConEntryId (dataConWorkId d)
+
+-- | Retrieve datacon worker entry variable name for the given datacon
+varForDataConWorker :: DataCon -> G JExpr
+varForDataConWorker d = varForDataConEntryId (dataConWorkId d)
+
+-- | Declare all js vars for the id
+declVarsForId :: Id -> G JStat
+declVarsForId i = case typeSize (idType i) of
+ 0 -> return mempty
+ 1 -> decl <$> identForId i
+ s -> mconcat <$> mapM (\n -> decl <$> identForIdN i n) [1..s]
+
diff --git a/compiler/GHC/StgToJS/Linker/Linker.hs b/compiler/GHC/StgToJS/Linker/Linker.hs
new file mode 100644
index 0000000000..6c4b011ce9
--- /dev/null
+++ b/compiler/GHC/StgToJS/Linker/Linker.hs
@@ -0,0 +1,953 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE LambdaCase #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Linker.Linker
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- GHCJS linker, collects dependencies from the object files
+-- which contain linkable units with dependency information
+--
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Linker.Linker
+ ( jsLinkBinary
+ , embedJsFile
+ )
+where
+
+import Prelude
+
+import GHC.Platform.Host (hostPlatformArchOS)
+
+import GHC.JS.Make
+import GHC.JS.Syntax
+
+import GHC.Driver.Session (DynFlags(..))
+import Language.Haskell.Syntax.Module.Name
+import GHC.SysTools.Cpp
+import GHC.SysTools
+
+import GHC.Linker.Static.Utils (exeFileName)
+
+import GHC.StgToJS.Linker.Types
+import GHC.StgToJS.Linker.Utils
+import GHC.StgToJS.Rts.Rts
+import GHC.StgToJS.Object
+import GHC.StgToJS.Types hiding (LinkableUnit)
+import GHC.StgToJS.Symbols
+import GHC.StgToJS.Printer
+import GHC.StgToJS.Arg
+import GHC.StgToJS.Closure
+
+import GHC.Unit.State
+import GHC.Unit.Env
+import GHC.Unit.Home
+import GHC.Unit.Types
+import GHC.Unit.Module (moduleStableString)
+
+import GHC.Utils.Outputable hiding ((<>))
+import GHC.Utils.Panic
+import GHC.Utils.Error
+import GHC.Utils.Logger (Logger, logVerbAtLeast)
+import GHC.Utils.Binary
+import qualified GHC.Utils.Ppr as Ppr
+import GHC.Utils.Monad
+import GHC.Utils.TmpFs
+
+import GHC.Types.Unique.Set
+
+import qualified GHC.SysTools.Ar as Ar
+
+import qualified GHC.Data.ShortText as ST
+import GHC.Data.FastString
+
+import Control.Concurrent.MVar
+import Control.Monad
+
+import Data.Array
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+import qualified Data.ByteString.Lazy.Char8 as BLC
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString as BS
+import Data.Function (on)
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IS
+import Data.IORef
+import Data.List ( partition, nub, intercalate, group, sort
+ , groupBy, intersperse,
+ )
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Maybe
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Word
+
+import System.IO
+import System.FilePath ((<.>), (</>), dropExtension, takeDirectory)
+import System.Directory ( createDirectoryIfMissing
+ , doesFileExist
+ , getCurrentDirectory
+ , Permissions(..)
+ , setPermissions
+ , getPermissions
+ )
+
+data LinkerStats = LinkerStats
+ { bytesPerModule :: !(Map Module Word64) -- ^ number of bytes linked per module
+ , packedMetaDataSize :: !Word64 -- ^ number of bytes for metadata
+ }
+
+newtype ArchiveState = ArchiveState { loadedArchives :: IORef (Map FilePath Ar.Archive) }
+
+emptyArchiveState :: IO ArchiveState
+emptyArchiveState = ArchiveState <$> newIORef M.empty
+
+jsLinkBinary
+ :: JSLinkConfig
+ -> StgToJSConfig
+ -> [FilePath]
+ -> Logger
+ -> DynFlags
+ -> UnitEnv
+ -> [FilePath]
+ -> [UnitId]
+ -> IO ()
+jsLinkBinary lc_cfg cfg js_srcs logger dflags u_env objs dep_pkgs
+ | lcNoJSExecutables lc_cfg = return ()
+ | otherwise = do
+ -- additional objects to link are passed as FileOption ldInputs...
+ let cmdline_objs = [ f | FileOption _ f <- ldInputs dflags ]
+ -- discriminate JavaScript sources from real object files.
+ (cmdline_js_srcs, cmdline_js_objs) <- partitionM isJsFile cmdline_objs
+ let
+ objs' = map ObjFile (objs ++ cmdline_js_objs)
+ js_srcs' = js_srcs ++ cmdline_js_srcs
+ isRoot _ = True
+ exe = jsExeFileName dflags
+
+ void $ link lc_cfg cfg logger u_env exe mempty dep_pkgs objs' js_srcs' isRoot mempty
+
+-- | link and write result to disk (jsexe directory)
+link :: JSLinkConfig
+ -> StgToJSConfig
+ -> Logger
+ -> UnitEnv
+ -> FilePath -- ^ output file/directory
+ -> [FilePath] -- ^ include path for home package
+ -> [UnitId] -- ^ packages to link
+ -> [LinkedObj] -- ^ the object files we're linking
+ -> [FilePath] -- ^ extra js files to include
+ -> (ExportedFun -> Bool) -- ^ functions from the objects to use as roots (include all their deps)
+ -> Set ExportedFun -- ^ extra symbols to link in
+ -> IO ()
+link lc_cfg cfg logger unit_env out _include units objFiles jsFiles isRootFun extraStaticDeps = do
+
+ -- create output directory
+ createDirectoryIfMissing False out
+
+ -------------------------------------------------------------
+ -- link all Haskell code (program + dependencies) into out.js
+
+ -- compute dependencies
+ (dep_map, dep_units, all_deps, _rts_wired_functions, dep_archives)
+ <- computeLinkDependencies cfg logger out unit_env units objFiles extraStaticDeps isRootFun
+
+ -- retrieve code for dependencies
+ mods <- collectDeps dep_map dep_units all_deps
+
+ -- LTO + rendering of JS code
+ link_stats <- withBinaryFile (out </> "out.js") WriteMode $ \h ->
+ renderLinker h mods jsFiles
+
+ -------------------------------------------------------------
+
+ -- dump foreign references file (.frefs)
+ unless (lcOnlyOut lc_cfg) $ do
+ let frefsFile = "out.frefs"
+ -- frefs = concatMap mc_frefs mods
+ jsonFrefs = mempty -- FIXME: toJson frefs
+
+ BL.writeFile (out </> frefsFile <.> "json") jsonFrefs
+ BL.writeFile (out </> frefsFile <.> "js")
+ ("h$checkForeignRefs(" <> jsonFrefs <> ");")
+
+ -- dump stats
+ unless (lcNoStats lc_cfg) $ do
+ let statsFile = "out.stats"
+ writeFile (out </> statsFile) (renderLinkerStats link_stats)
+
+ -- link generated RTS parts into rts.js
+ unless (lcNoRts lc_cfg) $ do
+ BL.writeFile (out </> "rts.js") ( BLC.pack rtsDeclsText
+ <> BLC.pack (rtsText cfg))
+
+ -- link dependencies' JS files into lib.js
+ withBinaryFile (out </> "lib.js") WriteMode $ \h -> do
+ forM_ dep_archives $ \archive_file -> do
+ Ar.Archive entries <- Ar.loadAr archive_file
+ forM_ entries $ \entry -> do
+ case getJsArchiveEntry entry of
+ Nothing -> return ()
+ Just bs -> do
+ B.hPut h bs
+ hPutChar h '\n'
+
+ -- link everything together into all.js
+ when (generateAllJs lc_cfg) $ do
+ _ <- combineFiles lc_cfg out
+ writeHtml out
+ writeRunMain out
+ writeRunner lc_cfg out
+ writeExterns out
+
+
+computeLinkDependencies
+ :: StgToJSConfig
+ -> Logger
+ -> String
+ -> UnitEnv
+ -> [UnitId]
+ -> [LinkedObj]
+ -> Set ExportedFun
+ -> (ExportedFun -> Bool)
+ -> IO (Map Module (Deps, DepsLocation), [UnitId], Set LinkableUnit, Set ExportedFun, [FilePath])
+computeLinkDependencies cfg logger target unit_env units objFiles extraStaticDeps isRootFun = do
+
+ (objDepsMap, objRequiredUnits) <- loadObjDeps objFiles
+
+ let roots = S.fromList . filter isRootFun $ concatMap (M.keys . depsHaskellExported . fst) (M.elems objDepsMap)
+ rootMods = map (moduleNameString . moduleName . head) . group . sort . map funModule . S.toList $ roots
+ objPkgs = map moduleUnitId $ nub (M.keys objDepsMap)
+
+ when (logVerbAtLeast logger 2) $ void $ do
+ compilationProgressMsg logger $ hcat
+ [ text "Linking ", text target, text " (", text (intercalate "," rootMods), char ')' ]
+ compilationProgressMsg logger $ hcat
+ [ text "objDepsMap ", ppr objDepsMap ]
+ compilationProgressMsg logger $ hcat
+ [ text "objFiles ", ppr objFiles ]
+
+ let (rts_wired_units, rts_wired_functions) = rtsDeps units
+
+ -- all the units we want to link together, without their dependencies
+ let root_units = filter (/= mainUnitId)
+ $ nub
+ $ rts_wired_units ++ reverse objPkgs ++ reverse units
+
+ -- all the units we want to link together, including their dependencies,
+ -- preload units, and backpack instantiations
+ all_units_infos <- mayThrowUnitErr (preloadUnitsInfo' unit_env root_units)
+
+ let all_units = fmap unitId all_units_infos
+
+ dep_archives <- getPackageArchives cfg unit_env all_units
+ env <- newGhcjsEnv
+ (archsDepsMap, archsRequiredUnits) <- loadArchiveDeps env dep_archives
+
+ when (logVerbAtLeast logger 2) $
+ logInfo logger $ hang (text "Linking with archives:") 2 (vcat (fmap text dep_archives))
+
+ -- compute dependencies
+ let dep_units = all_units ++ [homeUnitId (ue_unsafeHomeUnit $ unit_env)]
+ dep_map = objDepsMap `M.union` archsDepsMap
+ excluded_units = S.empty
+ dep_fun_roots = roots `S.union` rts_wired_functions `S.union` extraStaticDeps
+ dep_unit_roots = archsRequiredUnits ++ objRequiredUnits
+
+ all_deps <- getDeps (fmap fst dep_map) excluded_units dep_fun_roots dep_unit_roots
+
+ when (logVerbAtLeast logger 2) $
+ logInfo logger $ hang (text "Units to link:") 2 (vcat (fmap ppr dep_units))
+ -- logInfo logger $ hang (text "All deps:") 2 (vcat (fmap ppr (S.toList all_deps)))
+
+ return (dep_map, dep_units, all_deps, rts_wired_functions, dep_archives)
+
+
+-- | Compiled module
+data ModuleCode = ModuleCode
+ { mc_module :: !Module
+ , mc_js_code :: !JStat
+ , mc_exports :: !B.ByteString -- ^ rendered exports
+ , mc_closures :: ![ClosureInfo]
+ , mc_statics :: ![StaticInfo]
+ , mc_frefs :: ![ForeignJSRef]
+ }
+
+-- | ModuleCode after link with other modules.
+--
+-- It contains less information than ModuleCode because they have been commoned
+-- up into global "metadata" for the whole link.
+data CompactedModuleCode = CompactedModuleCode
+ { cmc_module :: !Module
+ , cmc_js_code :: !JStat
+ , cmc_exports :: !B.ByteString -- ^ rendered exports
+ }
+
+-- | Link modules and pretty-print them into the given Handle
+renderLinker
+ :: Handle
+ -> [ModuleCode] -- ^ linked code per module
+ -> [FilePath] -- ^ additional JS files
+ -> IO LinkerStats
+renderLinker h mods jsFiles = do
+
+ -- link modules
+ let (compacted_mods, meta) = linkModules mods
+
+ let
+ putBS = B.hPut h
+ putJS x = do
+ before <- hTell h
+ Ppr.printLeftRender h (pretty x)
+ hPutChar h '\n'
+ after <- hTell h
+ pure $! (after - before)
+
+ ---------------------------------------------------------
+ -- Pretty-print JavaScript code for all the dependencies.
+ --
+ -- We have to pretty-print at link time because we want to be able to perform
+ -- global link-time optimisations (e.g. renamings) on the whole generated JS
+ -- file.
+
+ -- modules themselves
+ mod_sizes <- forM compacted_mods $ \m -> do
+ !mod_size <- fromIntegral <$> putJS (cmc_js_code m)
+ let !mod_mod = cmc_module m
+ pure (mod_mod, mod_size)
+
+ -- commoned up metadata
+ !meta_length <- fromIntegral <$> putJS meta
+
+ -- module exports
+ mapM_ (putBS . cmc_exports) compacted_mods
+
+ -- explicit additional JS files
+ mapM_ (\i -> B.readFile i >>= putBS) jsFiles
+
+ -- stats
+ let link_stats = LinkerStats
+ { bytesPerModule = M.fromList mod_sizes
+ , packedMetaDataSize = meta_length
+ }
+
+ pure link_stats
+
+-- | Render linker stats
+renderLinkerStats :: LinkerStats -> String
+renderLinkerStats s =
+ intercalate "\n\n" [meta_stats, package_stats, module_stats] <> "\n\n"
+ where
+ meta = packedMetaDataSize s
+ meta_stats = "number of modules: " <> show (length bytes_per_mod)
+ <> "\npacked metadata: " <> show meta
+
+ bytes_per_mod = M.toList $ bytesPerModule s
+
+ show_unit (UnitId fs) = unpackFS fs
+
+ ps :: Map UnitId Word64
+ ps = M.fromListWith (+) . map (\(m,s) -> (moduleUnitId m,s)) $ bytes_per_mod
+
+ pad :: Int -> String -> String
+ pad n t = let l = length t
+ in if l < n then t <> replicate (n-l) ' ' else t
+
+ pkgMods :: [[(Module,Word64)]]
+ pkgMods = groupBy ((==) `on` (moduleUnitId . fst)) bytes_per_mod
+
+ showMod :: (Module, Word64) -> String
+ showMod (m,s) = pad 40 (" " <> moduleStableString m <> ":") <> show s <> "\n"
+
+ package_stats :: String
+ package_stats = "code size summary per package (in bytes):\n\n"
+ <> concatMap (\(p,s) -> pad 25 (show_unit p <> ":") <> show s <> "\n") (M.toList ps)
+
+ module_stats :: String
+ module_stats = "code size per module (in bytes):\n\n" <> unlines (map (concatMap showMod) pkgMods)
+
+
+getPackageArchives :: StgToJSConfig -> UnitEnv -> [UnitId] -> IO [FilePath]
+getPackageArchives cfg unit_env units =
+ filterM doesFileExist [ ST.unpack p </> "lib" ++ ST.unpack l ++ profSuff <.> "a"
+ | u <- units
+ , p <- getInstalledPackageLibDirs ue_state u
+ , l <- getInstalledPackageHsLibs ue_state u
+ ]
+ where
+ ue_state = ue_units unit_env
+
+ -- XXX the profiling library name is probably wrong now
+ profSuff | csProf cfg = "_p"
+ | otherwise = ""
+
+
+-- | Combine rts.js, lib.js, out.js to all.js that can be run
+-- directly with node.js or SpiderMonkey jsshell
+combineFiles :: JSLinkConfig
+ -> FilePath
+ -> IO ()
+combineFiles cfg fp = do
+ let files = map (fp </>) ["rts.js", "lib.js", "out.js"]
+ withBinaryFile (fp </> "all.js") WriteMode $ \h -> do
+ let cpy i = B.readFile i >>= B.hPut h
+ mapM_ cpy files
+ unless (lcNoHsMain cfg) $ do
+ B.hPut h runMainJS
+
+-- | write the index.html file that loads the program if it does not exit
+writeHtml
+ :: FilePath -- ^ output directory
+ -> IO ()
+writeHtml out = do
+ let htmlFile = out </> "index.html"
+ e <- doesFileExist htmlFile
+ unless e $
+ B.writeFile htmlFile templateHtml
+
+
+templateHtml :: B.ByteString
+templateHtml =
+ "<!DOCTYPE html>\n\
+ \<html>\n\
+ \ <head>\n\
+ \ </head>\n\
+ \ <body>\n\
+ \ </body>\n\
+ \ <script language=\"javascript\" src=\"all.js\" defer></script>\n\
+ \</html>"
+
+-- | write the runmain.js file that will be run with defer so that it runs after
+-- index.html is loaded
+writeRunMain
+ :: FilePath -- ^ output directory
+ -> IO ()
+writeRunMain out = do
+ let runMainFile = out </> "runmain.js"
+ e <- doesFileExist runMainFile
+ unless e $
+ B.writeFile runMainFile runMainJS
+
+runMainJS :: B.ByteString
+runMainJS = "h$main(h$mainZCZCMainzimain);\n"
+
+writeRunner :: JSLinkConfig -- ^ Settings
+ -> FilePath -- ^ Output directory
+ -> IO ()
+writeRunner _settings out = do
+ cd <- getCurrentDirectory
+ let arch_os = hostPlatformArchOS
+ let runner = cd </> exeFileName arch_os False (Just (dropExtension out))
+ srcFile = out </> "all" <.> "js"
+ nodePgm :: B.ByteString
+ nodePgm = "node"
+ src <- B.readFile (cd </> srcFile)
+ B.writeFile runner ("#!/usr/bin/env " <> nodePgm <> "\n" <> src)
+ perms <- getPermissions runner
+ setPermissions runner (perms {executable = True})
+
+rtsExterns :: FastString
+rtsExterns =
+ "// GHCJS RTS externs for closure compiler ADVANCED_OPTIMIZATIONS\n\n" <>
+ mconcat (map (\x -> "/** @type {*} */\nObject.d" <> mkFastString (show x) <> ";\n")
+ [(7::Int)..16384])
+
+writeExterns :: FilePath -> IO ()
+writeExterns out = writeFile (out </> "all.js.externs")
+ $ unpackFS rtsExterns
+
+-- | get all dependencies for a given set of roots
+getDeps :: Map Module Deps -- ^ loaded deps
+ -> Set LinkableUnit -- ^ don't link these blocks
+ -> Set ExportedFun -- ^ start here
+ -> [LinkableUnit] -- ^ and also link these
+ -> IO (Set LinkableUnit)
+getDeps loaded_deps base fun startlu = go' S.empty (S.fromList startlu) (S.toList fun)
+ where
+ go :: Set LinkableUnit
+ -> Set LinkableUnit
+ -> IO (Set LinkableUnit)
+ go result open = case S.minView open of
+ Nothing -> return result
+ Just (lu@(lmod,n), open') ->
+ case M.lookup lmod loaded_deps of
+ Nothing -> pprPanic "getDeps.go: object file not loaded for: " (pprModule lmod)
+ Just (Deps _ _ _ b) ->
+ let block = b!n
+ result' = S.insert lu result
+ in go' result'
+ (addOpen result' open' $
+ map (lmod,) (blockBlockDeps block)) (blockFunDeps block)
+
+ go' :: Set LinkableUnit
+ -> Set LinkableUnit
+ -> [ExportedFun]
+ -> IO (Set LinkableUnit)
+ go' result open [] = go result open
+ go' result open (f:fs) =
+ let key = funModule f
+ in case M.lookup key loaded_deps of
+ Nothing -> pprPanic "getDeps.go': object file not loaded for: " $ pprModule key
+ Just (Deps _m _r e _b) ->
+ let lun :: Int
+ lun = fromMaybe (pprPanic "exported function not found: " $ ppr f)
+ (M.lookup f e)
+ lu = (key, lun)
+ in go' result (addOpen result open [lu]) fs
+
+ addOpen :: Set LinkableUnit -> Set LinkableUnit -> [LinkableUnit]
+ -> Set LinkableUnit
+ addOpen result open newUnits =
+ let alreadyLinked s = S.member s result ||
+ S.member s open ||
+ S.member s base
+ in open `S.union` S.fromList (filter (not . alreadyLinked) newUnits)
+
+-- | collect dependencies for a set of roots
+collectDeps :: Map Module (Deps, DepsLocation) -- ^ Dependency map
+ -> [UnitId] -- ^ packages, code linked in this order
+ -> Set LinkableUnit -- ^ All dependencides
+ -> IO [ModuleCode]
+collectDeps mod_deps packages all_deps = do
+
+ -- read ghc-prim first, since we depend on that for static initialization
+ let packages' = uncurry (++) $ partition (== primUnitId) (nub packages)
+
+ units_by_module :: Map Module IntSet
+ units_by_module = M.fromListWith IS.union $
+ map (\(m,n) -> (m, IS.singleton n)) (S.toList all_deps)
+
+ mod_deps_bypkg :: Map UnitId [(Deps, DepsLocation)]
+ mod_deps_bypkg = M.fromListWith (++)
+ (map (\(m,v) -> (moduleUnitId m,[v])) (M.toList mod_deps))
+
+ ar_state <- emptyArchiveState
+ fmap (catMaybes . concat) . forM packages' $ \pkg ->
+ mapM (uncurry $ extractDeps ar_state units_by_module)
+ (fromMaybe [] $ M.lookup pkg mod_deps_bypkg)
+
+extractDeps :: ArchiveState
+ -> Map Module IntSet
+ -> Deps
+ -> DepsLocation
+ -> IO (Maybe ModuleCode)
+extractDeps ar_state units deps loc =
+ case M.lookup mod units of
+ Nothing -> return Nothing
+ Just mod_units -> Just <$> do
+ let selector n _ = fromIntegral n `IS.member` mod_units || isGlobalUnit (fromIntegral n)
+ case loc of
+ ObjectFile fp -> do
+ us <- readObjectUnits fp selector
+ pure (collectCode us)
+ ArchiveFile a -> do
+ obj <- readArObject ar_state mod a
+ us <- getObjectUnits obj selector
+ pure (collectCode us)
+ InMemory _n obj -> do
+ us <- getObjectUnits obj selector
+ pure (collectCode us)
+ where
+ mod = depsModule deps
+ newline = BC.pack "\n"
+ mk_exports = mconcat . intersperse newline . filter (not . BS.null) . map oiRaw
+ mk_js_code = mconcat . map oiStat
+ collectCode l = ModuleCode
+ { mc_module = mod
+ , mc_js_code = mk_js_code l
+ , mc_exports = mk_exports l
+ , mc_closures = concatMap oiClInfo l
+ , mc_statics = concatMap oiStatic l
+ , mc_frefs = concatMap oiFImports l
+ }
+
+readArObject :: ArchiveState -> Module -> FilePath -> IO Object
+readArObject ar_state mod ar_file = do
+ loaded_ars <- readIORef (loadedArchives ar_state)
+ (Ar.Archive entries) <- case M.lookup ar_file loaded_ars of
+ Just a -> pure a
+ Nothing -> do
+ a <- Ar.loadAr ar_file
+ modifyIORef (loadedArchives ar_state) (M.insert ar_file a)
+ pure a
+
+ -- look for the right object in archive
+ let go_entries = \case
+ -- XXX this shouldn't be an exception probably
+ [] -> panic $ "could not find object for module "
+ ++ moduleNameString (moduleName mod)
+ ++ " in "
+ ++ ar_file
+
+ (e:es) -> do
+ let bs = Ar.filedata e
+ bh <- unsafeUnpackBinBuffer bs
+ getObjectHeader bh >>= \case
+ Left _ -> go_entries es -- not a valid object entry
+ Right mod_name
+ | mod_name /= moduleName mod
+ -> go_entries es -- not the module we're looking for
+ | otherwise
+ -> getObjectBody bh mod_name -- found it
+
+ go_entries entries
+
+
+-- | A helper function to read system dependencies that are hardcoded
+diffDeps
+ :: [UnitId] -- ^ Packages that are already Linked
+ -> ([UnitId], Set ExportedFun) -- ^ New units and functions to link
+ -> ([UnitId], Set ExportedFun) -- ^ Diff
+diffDeps pkgs (deps_pkgs,deps_funs) =
+ ( filter linked_pkg deps_pkgs
+ , S.filter linked_fun deps_funs
+ )
+ where
+ linked_fun f = moduleUnitId (funModule f) `S.member` linked_pkgs
+ linked_pkg p = S.member p linked_pkgs
+ linked_pkgs = S.fromList pkgs
+
+-- | dependencies for the RTS, these need to be always linked
+rtsDeps :: [UnitId] -> ([UnitId], Set ExportedFun)
+rtsDeps pkgs = diffDeps pkgs $
+ ( [baseUnitId, primUnitId]
+ , S.fromList $ concat
+ [ mkBaseFuns "GHC.Conc.Sync"
+ ["reportError"]
+ , mkBaseFuns "Control.Exception.Base"
+ ["nonTermination"]
+ , mkBaseFuns "GHC.Exception.Type"
+ [ "SomeException"
+ , "underflowException"
+ , "overflowException"
+ , "divZeroException"
+ ]
+ , mkBaseFuns "GHC.TopHandler"
+ [ "runMainIO"
+ , "topHandler"
+ ]
+ , mkBaseFuns "GHC.Base"
+ ["$fMonadIO"]
+ , mkBaseFuns "GHC.Maybe"
+ [ "Nothing"
+ , "Just"
+ ]
+ , mkBaseFuns "GHC.Ptr"
+ ["Ptr"]
+ , mkBaseFuns "GHC.JS.Prim"
+ [ "JSVal"
+ , "JSException"
+ , "$fShowJSException"
+ , "$fExceptionJSException"
+ , "resolve"
+ , "resolveIO"
+ , "toIO"
+ ]
+ , mkBaseFuns "GHC.JS.Prim.Internal"
+ [ "wouldBlock"
+ , "blockedIndefinitelyOnMVar"
+ , "blockedIndefinitelyOnSTM"
+ , "ignoreException"
+ , "setCurrentThreadResultException"
+ , "setCurrentThreadResultValue"
+ ]
+ , mkPrimFuns "GHC.Types"
+ [ ":"
+ , "[]"
+ ]
+ , mkPrimFuns "GHC.Tuple.Prim"
+ [ "(,)"
+ , "(,,)"
+ , "(,,,)"
+ , "(,,,,)"
+ , "(,,,,,)"
+ , "(,,,,,,)"
+ , "(,,,,,,,)"
+ , "(,,,,,,,,)"
+ , "(,,,,,,,,,)"
+ ]
+ ]
+ )
+
+-- | Export the functions in base
+mkBaseFuns :: FastString -> [FastString] -> [ExportedFun]
+mkBaseFuns = mkExportedFuns baseUnitId
+
+-- | Export the Prim functions
+mkPrimFuns :: FastString -> [FastString] -> [ExportedFun]
+mkPrimFuns = mkExportedFuns primUnitId
+
+-- | Given a @UnitId@, a module name, and a set of symbols in the module,
+-- package these into an @ExportedFun@.
+mkExportedFuns :: UnitId -> FastString -> [FastString] -> [ExportedFun]
+mkExportedFuns uid mod_name symbols = map mk_fun symbols
+ where
+ mod = mkModule (RealUnit (Definite uid)) (mkModuleNameFS mod_name)
+ mk_fun sym = ExportedFun mod (LexicalFastString (mkJsSymbol True mod sym))
+
+-- | read all dependency data from the to-be-linked files
+loadObjDeps :: [LinkedObj] -- ^ object files to link
+ -> IO (Map Module (Deps, DepsLocation), [LinkableUnit])
+loadObjDeps objs = (prepareLoadedDeps . catMaybes) <$> mapM readDepsFromObj objs
+
+-- | Load dependencies for the Linker from Ar
+loadArchiveDeps :: GhcjsEnv
+ -> [FilePath]
+ -> IO ( Map Module (Deps, DepsLocation)
+ , [LinkableUnit]
+ )
+loadArchiveDeps env archives = modifyMVar (linkerArchiveDeps env) $ \m ->
+ case M.lookup archives' m of
+ Just r -> return (m, r)
+ Nothing -> loadArchiveDeps' archives >>= \r -> return (M.insert archives' r m, r)
+ where
+ archives' = S.fromList archives
+
+loadArchiveDeps' :: [FilePath]
+ -> IO ( Map Module (Deps, DepsLocation)
+ , [LinkableUnit]
+ )
+loadArchiveDeps' archives = do
+ archDeps <- forM archives $ \file -> do
+ (Ar.Archive entries) <- Ar.loadAr file
+ catMaybes <$> mapM (readEntry file) entries
+ return (prepareLoadedDeps $ concat archDeps)
+ where
+ readEntry :: FilePath -> Ar.ArchiveEntry -> IO (Maybe (Deps, DepsLocation))
+ readEntry ar_file ar_entry = do
+ let bs = Ar.filedata ar_entry
+ bh <- unsafeUnpackBinBuffer bs
+ getObjectHeader bh >>= \case
+ Left _ -> pure Nothing -- not a valid object entry
+ Right mod_name -> do
+ obj <- getObjectBody bh mod_name
+ let !deps = objDeps obj
+ pure $ Just (deps, ArchiveFile ar_file)
+
+-- | Predicate to check that an entry in Ar is a JS source
+-- and to return it without its header
+getJsArchiveEntry :: Ar.ArchiveEntry -> Maybe B.ByteString
+getJsArchiveEntry entry = getJsBS (Ar.filedata entry)
+
+-- | Predicate to check that a file is a JS source
+isJsFile :: FilePath -> IO Bool
+isJsFile fp = withBinaryFile fp ReadMode $ \h -> do
+ bs <- B.hGet h jsHeaderLength
+ pure (isJsBS bs)
+
+isJsBS :: B.ByteString -> Bool
+isJsBS bs = isJust (getJsBS bs)
+
+-- | Get JS source with its header (if it's one)
+getJsBS :: B.ByteString -> Maybe B.ByteString
+getJsBS bs = B.stripPrefix jsHeader bs
+
+-- Header added to JS sources to discriminate them from other object files.
+-- They all have .o extension but JS sources have this header.
+jsHeader :: B.ByteString
+jsHeader = "//JavaScript"
+
+jsHeaderLength :: Int
+jsHeaderLength = B.length jsHeader
+
+
+
+prepareLoadedDeps :: [(Deps, DepsLocation)]
+ -> ( Map Module (Deps, DepsLocation)
+ , [LinkableUnit]
+ )
+prepareLoadedDeps deps =
+ let req = concatMap (requiredUnits . fst) deps
+ depsMap = M.fromList $ map (\d -> (depsModule (fst d), d)) deps
+ in (depsMap, req)
+
+requiredUnits :: Deps -> [LinkableUnit]
+requiredUnits d = map (depsModule d,) (IS.toList $ depsRequired d)
+
+-- | read dependencies from an object that might have already been into memory
+-- pulls in all Deps from an archive
+readDepsFromObj :: LinkedObj -> IO (Maybe (Deps, DepsLocation))
+readDepsFromObj = \case
+ ObjLoaded name obj -> do
+ let !deps = objDeps obj
+ pure $ Just (deps,InMemory name obj)
+ ObjFile file -> do
+ readObjectDeps file >>= \case
+ Nothing -> pure Nothing
+ Just deps -> pure $ Just (deps,ObjectFile file)
+
+
+-- | Embed a JS file into a .o file
+--
+-- The JS file is merely copied into a .o file with an additional header
+-- ("//Javascript") in order to be recognized later on.
+--
+-- JS files may contain option pragmas of the form: //#OPTIONS:
+-- For now, only the CPP option is supported. If the CPP option is set, we
+-- append some common CPP definitions to the file and call cpp on it.
+embedJsFile :: Logger -> DynFlags -> TmpFs -> UnitEnv -> FilePath -> FilePath -> IO ()
+embedJsFile logger dflags tmpfs unit_env input_fn output_fn = do
+ let profiling = False -- FIXME: add support for profiling way
+
+ createDirectoryIfMissing True (takeDirectory output_fn)
+
+ -- the header lets the linker recognize processed JavaScript files
+ -- But don't add JavaScript header to object files!
+
+ is_js_obj <- if True
+ then pure False
+ else isJsObjectFile input_fn
+ -- FIXME (Sylvain 2022-09): this call makes the
+ -- testsuite go into a loop, I don't know why yet!
+ -- Disabling it for now.
+
+ if is_js_obj
+ then copyWithHeader "" input_fn output_fn
+ else do
+ -- header appended to JS files stored as .o to recognize them.
+ let header = "//JavaScript\n"
+ jsFileNeedsCpp input_fn >>= \case
+ False -> copyWithHeader header input_fn output_fn
+ True -> do
+
+ -- append common CPP definitions to the .js file.
+ -- They define macros that avoid directly wiring zencoded names
+ -- in RTS JS files
+ pp_fn <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "js"
+ payload <- B.readFile input_fn
+ B.writeFile pp_fn (commonCppDefs profiling <> payload)
+
+ -- run CPP on the input JS file
+ js_fn <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "js"
+ let
+ cpp_opts = CppOpts
+ { cppUseCc = True
+ , cppLinePragmas = False -- LINE pragmas aren't JS compatible
+ }
+ doCpp logger
+ tmpfs
+ dflags
+ unit_env
+ cpp_opts
+ pp_fn
+ js_fn
+ -- add header to recognize the object as a JS file
+ copyWithHeader header js_fn output_fn
+
+jsFileNeedsCpp :: FilePath -> IO Bool
+jsFileNeedsCpp fn = do
+ opts <- getOptionsFromJsFile fn
+ pure (CPP `elem` opts)
+
+-- | Link module codes.
+--
+-- Performs link time optimizations and produces one JStat per module plus some
+-- commoned up initialization code.
+linkModules :: [ModuleCode] -> ([CompactedModuleCode], JStat)
+linkModules mods = (compact_mods, meta)
+ where
+ compact_mods = map compact mods
+
+ -- here GHCJS used to:
+ -- - deduplicate declarations
+ -- - rename local variables into shorter ones
+ -- - compress initialization data
+ -- but we haven't ported it (yet).
+ compact m = CompactedModuleCode
+ { cmc_js_code = mc_js_code m
+ , cmc_module = mc_module m
+ , cmc_exports = mc_exports m
+ }
+
+ -- common up statics: different bindings may reference the same statics, we
+ -- filter them here to initialize them once
+ statics = nubStaticInfo (concatMap mc_statics mods)
+
+ infos = concatMap mc_closures mods
+ meta = mconcat
+ -- render metadata as individual statements
+ [ mconcat (map staticDeclStat statics)
+ , mconcat (map staticInitStat statics)
+ , mconcat (map (closureInfoStat True) infos)
+ ]
+
+-- | Only keep a single StaticInfo with a given name
+nubStaticInfo :: [StaticInfo] -> [StaticInfo]
+nubStaticInfo = go emptyUniqSet
+ where
+ go us = \case
+ [] -> []
+ (x:xs) ->
+ -- only match on siVar. There is no reason for the initializing value to
+ -- be different for the same global name.
+ let name = siVar x
+ in if elementOfUniqSet name us
+ then go us xs
+ else x : go (addOneToUniqSet us name) xs
+
+-- | Initialize a global object.
+--
+-- All global objects have to be declared (staticInfoDecl) first.
+staticInitStat :: StaticInfo -> JStat
+staticInitStat (StaticInfo i sv mcc) =
+ case sv of
+ StaticData con args -> appS "h$sti" $ add_cc_arg
+ [ var i
+ , var con
+ , jsStaticArgs args
+ ]
+ StaticFun f args -> appS "h$sti" $ add_cc_arg
+ [ var i
+ , var f
+ , jsStaticArgs args
+ ]
+ StaticList args mt -> appS "h$stl" $ add_cc_arg
+ [ var i
+ , jsStaticArgs args
+ , toJExpr $ maybe null_ (toJExpr . TxtI) mt
+ ]
+ StaticThunk (Just (f,args)) -> appS "h$stc" $ add_cc_arg
+ [ var i
+ , var f
+ , jsStaticArgs args
+ ]
+ _ -> mempty
+ where
+ -- add optional cost-center argument
+ add_cc_arg as = case mcc of
+ Nothing -> as
+ Just cc -> as ++ [toJExpr cc]
+
+-- | declare and do first-pass init of a global object (create JS object for heap objects)
+staticDeclStat :: StaticInfo -> JStat
+staticDeclStat (StaticInfo global_name static_value _) = decl
+ where
+ global_ident = TxtI global_name
+ decl_init v = global_ident ||= v
+ decl_no_init = appS "h$di" [toJExpr global_ident]
+
+ decl = case static_value of
+ StaticUnboxed u -> decl_init (unboxed_expr u)
+ StaticThunk Nothing -> decl_no_init -- CAF initialized in an alternative way
+ _ -> decl_init (app "h$d" [])
+
+ unboxed_expr = \case
+ StaticUnboxedBool b -> app "h$p" [toJExpr b]
+ StaticUnboxedInt i -> app "h$p" [toJExpr i]
+ StaticUnboxedDouble d -> app "h$p" [toJExpr (unSaneDouble d)]
+ StaticUnboxedString str -> app "h$rawStringData" [ValExpr (to_byte_list str)]
+ StaticUnboxedStringOffset {} -> 0
+
+ to_byte_list = JList . map (Int . fromIntegral) . BS.unpack
diff --git a/compiler/GHC/StgToJS/Linker/Types.hs b/compiler/GHC/StgToJS/Linker/Types.hs
new file mode 100644
index 0000000000..9e1714fc00
--- /dev/null
+++ b/compiler/GHC/StgToJS/Linker/Types.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PartialTypeSignatures #-}
+{-# LANGUAGE LambdaCase #-}
+
+{-# OPTIONS_GHC -Wno-orphans #-} -- for Ident's Binary instance
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Linker.Types
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Linker.Types
+ ( GhcjsEnv (..)
+ , newGhcjsEnv
+ , JSLinkConfig (..)
+ , defaultJSLinkConfig
+ , generateAllJs
+ , LinkedObj (..)
+ , LinkableUnit
+ )
+where
+
+import GHC.StgToJS.Object
+
+import GHC.Unit.Types
+import GHC.Utils.Outputable (hsep,Outputable(..),text,ppr)
+
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Set (Set)
+
+import Control.Concurrent.MVar
+
+import System.IO
+
+import Prelude
+
+--------------------------------------------------------------------------------
+-- Linker Config
+--------------------------------------------------------------------------------
+
+data JSLinkConfig = JSLinkConfig
+ { lcNoJSExecutables :: Bool
+ , lcNoHsMain :: Bool
+ , lcOnlyOut :: Bool
+ , lcNoRts :: Bool
+ , lcNoStats :: Bool
+ }
+
+-- | we generate a runnable all.js only if we link a complete application,
+-- no incremental linking and no skipped parts
+generateAllJs :: JSLinkConfig -> Bool
+generateAllJs s = not (lcOnlyOut s) && not (lcNoRts s)
+
+defaultJSLinkConfig :: JSLinkConfig
+defaultJSLinkConfig = JSLinkConfig
+ { lcNoJSExecutables = False
+ , lcNoHsMain = False
+ , lcOnlyOut = False
+ , lcNoRts = False
+ , lcNoStats = False
+ }
+
+--------------------------------------------------------------------------------
+-- Linker Environment
+--------------------------------------------------------------------------------
+
+-- | A @LinkableUnit@ is a pair of a module and the index of the block in the
+-- object file
+type LinkableUnit = (Module, Int)
+
+-- | An object file that's either already in memory (with name) or on disk
+data LinkedObj
+ = ObjFile FilePath -- ^ load from this file
+ | ObjLoaded String Object -- ^ already loaded: description and payload
+
+instance Outputable LinkedObj where
+ ppr = \case
+ ObjFile fp -> hsep [text "ObjFile", text fp]
+ ObjLoaded s o -> hsep [text "ObjLoaded", text s, ppr (objModuleName o)]
+
+data GhcjsEnv = GhcjsEnv
+ { linkerArchiveDeps :: MVar (Map (Set FilePath)
+ (Map Module (Deps, DepsLocation)
+ , [LinkableUnit]
+ )
+ )
+ }
+
+-- | return a fresh @GhcjsEnv@
+newGhcjsEnv :: IO GhcjsEnv
+newGhcjsEnv = GhcjsEnv <$> newMVar M.empty
diff --git a/compiler/GHC/StgToJS/Linker/Utils.hs b/compiler/GHC/StgToJS/Linker/Utils.hs
new file mode 100644
index 0000000000..0733b73ff6
--- /dev/null
+++ b/compiler/GHC/StgToJS/Linker/Utils.hs
@@ -0,0 +1,308 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE MultiWayIf #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Linker.Utils
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Various utilies used in the JS Linker
+--
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Linker.Utils
+ ( getOptionsFromJsFile
+ , JSOption(..)
+ , jsExeFileName
+ , getInstalledPackageLibDirs
+ , getInstalledPackageHsLibs
+ , commonCppDefs
+ )
+where
+
+import System.FilePath
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as Char8
+import Data.ByteString (ByteString)
+
+import GHC.Driver.Session
+
+import GHC.Data.ShortText
+import GHC.Unit.State
+import GHC.Unit.Types
+
+import GHC.StgToJS.Types
+
+import Prelude
+import GHC.Platform
+import Data.List (isPrefixOf)
+import System.IO
+import Data.Char (isSpace)
+import qualified Control.Exception as Exception
+
+-- | Retrieve library directories provided by the @UnitId@ in @UnitState@
+getInstalledPackageLibDirs :: UnitState -> UnitId -> [ShortText]
+getInstalledPackageLibDirs us = maybe mempty unitLibraryDirs . lookupUnitId us
+
+-- | Retrieve the names of the libraries provided by @UnitId@
+getInstalledPackageHsLibs :: UnitState -> UnitId -> [ShortText]
+getInstalledPackageHsLibs us = maybe mempty unitLibraries . lookupUnitId us
+
+-- | A constant holding the JavaScript executable Filename extension
+jsexeExtension :: String
+jsexeExtension = "jsexe"
+
+-- | CPP definitions that are inserted into every .pp file
+commonCppDefs :: Bool -> ByteString
+commonCppDefs profiling = case profiling of
+ True -> commonCppDefs_profiled
+ False -> commonCppDefs_vanilla
+
+-- | CPP definitions for normal operation and profiling. Use CAFs for
+-- commonCppDefs_* so that they are shared for every CPP file
+commonCppDefs_vanilla, commonCppDefs_profiled :: ByteString
+commonCppDefs_vanilla = genCommonCppDefs False
+commonCppDefs_profiled = genCommonCppDefs True
+
+-- | Generate CPP Definitions depending on a profiled or normal build. This
+-- occurs at link time.
+genCommonCppDefs :: Bool -> ByteString
+genCommonCppDefs profiling = mconcat
+ [
+ -- constants
+ let mk_int_def n v = "#define " <> Char8.pack n <> " (" <> Char8.pack (show v) <> ")\n"
+ -- generate "#define CLOSURE_TYPE_xyz (num)" defines
+ mk_closure_def t = mk_int_def (ctJsName t) (ctNum t)
+ closure_defs = map mk_closure_def [minBound..maxBound]
+ -- generate "#define THREAD_xyz_xyz (num)" defines
+ mk_thread_def t = mk_int_def (threadStatusJsName t) (threadStatusNum t)
+ thread_defs = map mk_thread_def [minBound..maxBound]
+ in mconcat (closure_defs ++ thread_defs)
+
+ -- low-level heap object manipulation macros
+ , if profiling
+ then mconcat
+ [ "#define MK_TUP2(x1,x2) (h$c2(h$ghczmprimZCGHCziTupleziPrimziZLz2cUZR_con_e,(x1),(x2),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP3(x1,x2,x3) (h$c3(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUZR_con_e,(x1),(x2),(x3),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP4(x1,x2,x3,x4) (h$c4(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP5(x1,x2,x3,x4,x5) (h$c5(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP6(x1,x2,x3,x4,x5,x6) (h$c6(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP7(x1,x2,x3,x4,x5,x6,x7) (h$c7(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) (h$c8(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) (h$c9(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ , "#define MK_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) (h$c10(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(x10),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"
+ ]
+ else mconcat
+ [ "#define MK_TUP2(x1,x2) (h$c2(h$ghczmprimZCGHCziTupleziPrimziZLz2cUZR_con_e,(x1),(x2)))\n"
+ , "#define MK_TUP3(x1,x2,x3) (h$c3(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUZR_con_e,(x1),(x2),(x3)))\n"
+ , "#define MK_TUP4(x1,x2,x3,x4) (h$c4(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4)))\n"
+ , "#define MK_TUP5(x1,x2,x3,x4,x5) (h$c5(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5)))\n"
+ , "#define MK_TUP6(x1,x2,x3,x4,x5,x6) (h$c6(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6)))\n"
+ , "#define MK_TUP7(x1,x2,x3,x4,x5,x6,x7) (h$c7(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7)))\n"
+ , "#define MK_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) (h$c8(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8)))\n"
+ , "#define MK_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) (h$c9(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9)))\n"
+ , "#define MK_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) (h$c10(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(x10)))\n"
+ ]
+
+ , "#define TUP2_1(x) ((x).d1)\n"
+ , "#define TUP2_2(x) ((x).d2)\n"
+
+ -- GHCJS.Prim.JSVal
+ , if profiling
+ then "#define MK_JSVAL(x) (h$baseZCGHCziJSziPrimziJSVal_con_e, (x), h$CCS_SYSTEM)\n"
+ else "#define MK_JSVAL(x) (h$baseZCGHCziJSziPrimziJSVal_con_e, (x))\n"
+ , "#define JSVAL_VAL(x) ((x).d1)\n"
+
+ -- GHCJS.Prim.JSException
+ , if profiling
+ then "#define MK_JSEXCEPTION(msg,hsMsg) (h$c2(h$baseZCGHCziJSziPrimziJSException_con_e,(msg),(hsMsg),h$CCS_SYSTEM))\n"
+ else "#define MK_JSEXCEPTION(msg,hsMsg) (h$c2(h$baseZCGHCziJSziPrimziJSException_con_e,(msg),(hsMsg)))\n"
+
+ -- Exception dictionary for JSException
+ , "#define HS_JSEXCEPTION_EXCEPTION h$baseZCGHCziJSziPrimzizdfExceptionJSException\n"
+
+ -- SomeException
+ , if profiling
+ then "#define MK_SOMEEXCEPTION(dict,except) (h$c2(h$baseZCGHCziExceptionziTypeziSomeException_con_e,(dict),(except),h$CCS_SYSTEM))\n"
+ else "#define MK_SOMEEXCEPTION(dict,except) (h$c2(h$baseZCGHCziExceptionziTypeziSomeException_con_e,(dict),(except)))\n"
+
+ -- GHC.Ptr.Ptr
+ , if profiling
+ then "#define MK_PTR(val,offset) (h$c2(h$baseZCGHCziPtrziPtr_con_e, (val), (offset), h$CCS_SYSTEM))\n"
+ else "#define MK_PTR(val,offset) (h$c2(h$baseZCGHCziPtrziPtr_con_e, (val), (offset)))\n"
+
+ -- Data.Maybe.Maybe
+ , "#define HS_NOTHING h$baseZCGHCziMaybeziNothing\n"
+ , "#define IS_NOTHING(cl) ((cl).f === h$baseZCGHCziMaybeziNothing_con_e)\n"
+ , "#define IS_JUST(cl) ((cl).f === h$baseZCGHCziMaybeziJust_con_e)\n"
+ , "#define JUST_VAL(jj) ((jj).d1)\n"
+ -- "#define HS_NOTHING h$nothing\n"
+ , if profiling
+ then "#define MK_JUST(val) (h$c1(h$baseZCGHCziMaybeziJust_con_e, (val), h$CCS_SYSTEM))\n"
+ else "#define MK_JUST(val) (h$c1(h$baseZCGHCziMaybeziJust_con_e, (val)))\n"
+
+ -- Data.List
+ , "#define HS_NIL h$ghczmprimZCGHCziTypesziZMZN\n"
+ , "#define HS_NIL_CON h$ghczmprimZCGHCziTypesziZMZN_con_e\n"
+ , "#define IS_CONS(cl) ((cl).f === h$ghczmprimZCGHCziTypesziZC_con_e)\n"
+ , "#define IS_NIL(cl) ((cl).f === h$ghczmprimZCGHCziTypesziZMZN_con_e)\n"
+ , "#define CONS_HEAD(cl) ((cl).d1)\n"
+ , "#define CONS_TAIL(cl) ((cl).d2)\n"
+ , if profiling
+ then mconcat
+ [ "#define MK_CONS(head,tail) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail), h$CCS_SYSTEM))\n"
+ , "#define MK_CONS_CC(head,tail,cc) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail), (cc)))\n"
+ ]
+ else mconcat
+ [ "#define MK_CONS(head,tail) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail)))\n"
+ , "#define MK_CONS_CC(head,tail,cc) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail)))\n"
+ ]
+
+ -- Data.Text
+ , "#define DATA_TEXT_ARRAY(x) ((x).d1)\n"
+ , "#define DATA_TEXT_OFFSET(x) ((x).d2.d1)\n"
+ , "#define DATA_TEXT_LENGTH(x) ((x).d2.d2)\n"
+
+ -- Data.Text.Lazy
+ , "#define LAZY_TEXT_IS_CHUNK(x) ((x).f.a === 2)\n"
+ , "#define LAZY_TEXT_IS_NIL(x) ((x).f.a === 1)\n"
+ , "#define LAZY_TEXT_CHUNK_HEAD(x) ((x))\n"
+ , "#define LAZY_TEXT_CHUNK_TAIL(x) ((x).d2.d3)\n"
+
+ -- black holes
+ -- can we skip the indirection for black holes?
+ , "#define IS_BLACKHOLE(x) (typeof (x) === 'object' && (x) && (x).f && (x).f.t === CLOSURE_TYPE_BLACKHOLE)\n"
+ , "#define BLACKHOLE_TID(bh) ((bh).d1)\n"
+ , "#define SET_BLACKHOLE_TID(bh,tid) ((bh).d1 = (tid))\n"
+ , "#define BLACKHOLE_QUEUE(bh) ((bh).d2)\n"
+ , "#define SET_BLACKHOLE_QUEUE(bh,val) ((bh).d2 = (val))\n"
+
+ -- resumable thunks
+ , "#define MAKE_RESUMABLE(closure,stack) { (closure).f = h$resume_e; (closure).d1 = (stack), (closure).d2 = null; }\n"
+
+ -- general deconstruction
+ , "#define IS_THUNK(x) ((x).f.t === CLOSURE_TYPE_THUNK)\n"
+ , "#define CONSTR_TAG(x) ((x).f.a)\n"
+
+ -- retrieve a numeric value that's possibly stored as an indirection
+ , "#define IS_WRAPPED_NUMBER(val) ((typeof(val)==='object')&&(val).f === h$unbox_e)\n"
+ , "#define UNWRAP_NUMBER(val) ((typeof(val) === 'number')?(val):(val).d1)\n"
+
+ -- generic lazy values
+ , if profiling
+ then mconcat
+ [ "#define MK_LAZY(fun) (h$c1(h$lazy_e, (fun), h$CCS_SYSTEM))\n"
+ , "#define MK_LAZY_CC(fun,cc) (h$c1(h$lazy_e, (fun), (cc)))\n"
+ ]
+ else mconcat
+ [ "#define MK_LAZY(fun) (h$c1(h$lazy_e, (fun)))\n"
+ , "#define MK_LAZY_CC(fun,cc) (h$c1(h$lazy_e, (fun)))\n"
+ ]
+
+ -- generic data constructors and selectors
+ , if profiling
+ then mconcat
+ [ "#define MK_DATA1_1(val) (h$c1(h$data1_e, (val), h$CCS_SYSTEM))\n"
+ , "#define MK_DATA1_2(val1,val2) (h$c2(h$data1_e, (val1), (val2), h$CCS_SYSTEM))\n"
+ , "#define MK_DATA2_1(val) (h$c1(h$data2_e, (val), h$CCS_SYSTEM))\n"
+ , "#define MK_DATA2_2(val1,val2) (h$c2(h$data1_e, (val1), (val2), h$CCS_SYSTEM))\n"
+ , "#define MK_SELECT1(val) (h$c1(h$select1_e, (val), h$CCS_SYSTEM))\n"
+ , "#define MK_SELECT2(val) (h$c1(h$select2_e, (val), h$CCS_SYSTEM))\n"
+ , "#define MK_AP1(fun,val) (h$c2(h$ap1_e, (fun), (val), h$CCS_SYSTEM))\n"
+ , "#define MK_AP2(fun,val1,val2) (h$c3(h$ap2_e, (fun), (val1), (val2), h$CCS_SYSTEM))\n"
+ , "#define MK_AP3(fun,val1,val2,val3) (h$c4(h$ap3_e, (fun), (val1), (val2), (val3), h$CCS_SYSTEM))\n"
+ ]
+ else mconcat
+ [ "#define MK_DATA1_1(val) (h$c1(h$data1_e, (val)))\n"
+ , "#define MK_DATA1_2(val1,val2) (h$c2(h$data1_e, (val1), (val2)))\n"
+ , "#define MK_DATA2_1(val) (h$c1(h$data2_e, (val)))\n"
+ , "#define MK_DATA2_2(val1,val2) (h$c2(h$data2_e, (val1), (val2)))\n"
+ , "#define MK_SELECT1(val) (h$c1(h$select1_e, (val)))\n"
+ , "#define MK_SELECT2(val) (h$c1(h$select2_e, (val)))\n"
+ , "#define MK_AP1(fun,val) (h$c2(h$ap1_e,(fun),(val)))\n"
+ , "#define MK_AP2(fun,val1,val2) (h$c3(h$ap2_e,(fun),(val1),(val2)))\n"
+ , "#define MK_AP3(fun,val1,val2,val3) (h$c4(h$ap3_e, (fun), (val1), (val2), (val3)))\n"
+ ]
+
+ -- unboxed tuple returns
+ -- , "#define RETURN_UBX_TUP1(x) return x;\n"
+ , "#define RETURN_UBX_TUP2(x1,x2) { h$ret1 = (x2); return (x1); }\n"
+ , "#define RETURN_UBX_TUP3(x1,x2,x3) { h$ret1 = (x2); h$ret2 = (x3); return (x1); }\n"
+ , "#define RETURN_UBX_TUP4(x1,x2,x3,x4) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); return (x1); }\n"
+ , "#define RETURN_UBX_TUP5(x1,x2,x3,x4,x5) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); return (x1); }\n"
+ , "#define RETURN_UBX_TUP6(x1,x2,x3,x4,x5,x6) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); return (x1); }\n"
+ , "#define RETURN_UBX_TUP7(x1,x2,x3,x4,x5,x6,x7) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); return (x1); }\n"
+ , "#define RETURN_UBX_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); return (x1); }\n"
+ , "#define RETURN_UBX_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); h$ret8 = (x9); return (x1); }\n"
+ , "#define RETURN_UBX_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); h$ret8 = (x9); h$ret9 = (x10); return (x1); }\n"
+
+ , "#define CALL_UBX_TUP2(r1,r2,c) { (r1) = (c); (r2) = h$ret1; }\n"
+ , "#define CALL_UBX_TUP3(r1,r2,r3,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; }\n"
+ , "#define CALL_UBX_TUP4(r1,r2,r3,r4,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; }\n"
+ , "#define CALL_UBX_TUP5(r1,r2,r3,r4,r5,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; }\n"
+ , "#define CALL_UBX_TUP6(r1,r2,r3,r4,r5,r6,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; }\n"
+ , "#define CALL_UBX_TUP7(r1,r2,r3,r4,r5,r6,r7,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; }\n"
+ , "#define CALL_UBX_TUP8(r1,r2,r3,r4,r5,r6,r7,r8,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; }\n"
+ , "#define CALL_UBX_TUP9(r1,r2,r3,r4,r5,r6,r7,r8,r9,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; (r9) = h$ret8; }\n"
+ , "#define CALL_UBX_TUP10(r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; (r9) = h$ret8; (r10) = h$ret9; }\n"
+ ]
+
+-- | Construct the Filename for the "binary" of Haskell code compiled to
+-- JavaScript.
+jsExeFileName :: DynFlags -> FilePath
+jsExeFileName dflags
+ | Just s <- outputFile_ dflags =
+ -- unmunge the extension
+ let s' = dropPrefix "js_" (drop 1 $ takeExtension s)
+ in if Prelude.null s'
+ then dropExtension s <.> jsexeExtension
+ else dropExtension s <.> s'
+ | otherwise =
+ if platformOS (targetPlatform dflags) == OSMinGW32
+ then "main.jsexe"
+ else "a.jsexe"
+ where
+ dropPrefix prefix xs
+ | prefix `isPrefixOf` xs = drop (length prefix) xs
+ | otherwise = xs
+
+
+-- | Parse option pragma in JS file
+getOptionsFromJsFile :: FilePath -- ^ Input file
+ -> IO [JSOption] -- ^ Parsed options, if any.
+getOptionsFromJsFile filename
+ = Exception.bracket
+ (openBinaryFile filename ReadMode)
+ hClose
+ getJsOptions
+
+data JSOption = CPP deriving (Eq, Ord)
+
+getJsOptions :: Handle -> IO [JSOption]
+getJsOptions handle = do
+ hSetEncoding handle utf8
+ prefix' <- B.hGet handle prefixLen
+ if prefix == prefix'
+ then parseJsOptions <$> hGetLine handle
+ else pure []
+ where
+ prefix :: B.ByteString
+ prefix = "//#OPTIONS:"
+ prefixLen = B.length prefix
+
+parseJsOptions :: String -> [JSOption]
+parseJsOptions xs = go xs
+ where
+ trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
+ go [] = []
+ go xs = let (tok, rest) = break (== ',') xs
+ tok' = trim tok
+ rest' = drop 1 rest
+ in if | tok' == "CPP" -> CPP : go rest'
+ | otherwise -> go rest'
diff --git a/compiler/GHC/StgToJS/Literal.hs b/compiler/GHC/StgToJS/Literal.hs
new file mode 100644
index 0000000000..13549cd324
--- /dev/null
+++ b/compiler/GHC/StgToJS/Literal.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.Literal
+ ( genLit
+ , genStaticLit
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Types
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Ids
+import GHC.StgToJS.Symbols
+
+import GHC.Data.FastString
+import GHC.Types.Literal
+import GHC.Types.Basic
+import GHC.Utils.Misc
+import GHC.Utils.Panic
+import GHC.Utils.Outputable
+import GHC.Float
+
+import Data.Bits as Bits
+import Data.Char (ord)
+
+-- | Generate JS expressions for a Literal
+--
+-- Literals represented with 2 values:
+-- * Addr# (Null and Strings): array and offset
+-- * 64-bit values: high 32-bit, low 32-bit
+-- * labels: call to h$mkFunctionPtr and 0, or function name and 0
+genLit :: HasDebugCallStack => Literal -> G [JExpr]
+genLit = \case
+ LitChar c -> return [ toJExpr (ord c) ]
+ LitString str ->
+ freshIdent >>= \strLit@(TxtI strLitT) ->
+ freshIdent >>= \strOff@(TxtI strOffT) -> do
+ emitStatic strLitT (StaticUnboxed (StaticUnboxedString str)) Nothing
+ emitStatic strOffT (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing
+ return [ ValExpr (JVar strLit), ValExpr (JVar strOff) ]
+ LitNullAddr -> return [ null_, ValExpr (JInt 0) ]
+ LitNumber nt v -> case nt of
+ LitNumInt -> return [ toJExpr v ]
+ LitNumInt8 -> return [ toJExpr v ]
+ LitNumInt16 -> return [ toJExpr v ]
+ LitNumInt32 -> return [ toJExpr v ]
+ LitNumInt64 -> return [ toJExpr (Bits.shiftR v 32), toU32Expr v ]
+ LitNumWord -> return [ toU32Expr v ]
+ LitNumWord8 -> return [ toU32Expr v ]
+ LitNumWord16 -> return [ toU32Expr v ]
+ LitNumWord32 -> return [ toU32Expr v ]
+ LitNumWord64 -> return [ toU32Expr (Bits.shiftR v 32), toU32Expr v ]
+ LitNumBigNat -> panic "genLit: unexpected BigNat that should have been removed in CorePrep"
+ LitFloat r -> return [ toJExpr (r2f r) ]
+ LitDouble r -> return [ toJExpr (r2d r) ]
+ LitLabel name _size fod
+ | fod == IsFunction -> return [ ApplExpr (var "h$mkFunctionPtr")
+ [var (mkRawSymbol True name)]
+ , ValExpr (JInt 0)
+ ]
+ | otherwise -> return [ toJExpr (TxtI (mkRawSymbol True name))
+ , ValExpr (JInt 0)
+ ]
+ LitRubbish {} -> return [ null_ ]
+
+-- | generate a literal for the static init tables
+genStaticLit :: Literal -> G [StaticLit]
+genStaticLit = \case
+ LitChar c -> return [ IntLit (fromIntegral $ ord c) ]
+ LitString str
+ | True -> return [ StringLit (mkFastStringByteString str), IntLit 0]
+ -- \| invalid UTF8 -> return [ BinLit str, IntLit 0]
+ LitNullAddr -> return [ NullLit, IntLit 0 ]
+ LitNumber nt v -> case nt of
+ LitNumInt -> return [ IntLit v ]
+ LitNumInt8 -> return [ IntLit v ]
+ LitNumInt16 -> return [ IntLit v ]
+ LitNumInt32 -> return [ IntLit v ]
+ LitNumInt64 -> return [ IntLit (v `Bits.shiftR` 32), toU32Lit v ]
+ LitNumWord -> return [ toU32Lit v ]
+ LitNumWord8 -> return [ toU32Lit v ]
+ LitNumWord16 -> return [ toU32Lit v ]
+ LitNumWord32 -> return [ toU32Lit v ]
+ LitNumWord64 -> return [ toU32Lit (v `Bits.shiftR` 32), toU32Lit v ]
+ LitNumBigNat -> panic "genStaticLit: unexpected BigNat that should have been removed in CorePrep"
+ LitFloat r -> return [ DoubleLit . SaneDouble . r2f $ r ]
+ LitDouble r -> return [ DoubleLit . SaneDouble . r2d $ r ]
+ LitLabel name _size fod -> return [ LabelLit (fod == IsFunction) (mkRawSymbol True name)
+ , IntLit 0 ]
+ l -> pprPanic "genStaticLit" (ppr l)
+
+-- make an unsigned 32 bit number from this unsigned one, lower 32 bits
+toU32Expr :: Integer -> JExpr
+toU32Expr i = Int (i Bits..&. 0xFFFFFFFF) .>>>. 0
+
+-- make an unsigned 32 bit number from this unsigned one, lower 32 bits
+toU32Lit :: Integer -> StaticLit
+toU32Lit i = IntLit (i Bits..&. 0xFFFFFFFF)
+
+r2d :: Rational -> Double
+r2d = realToFrac
+
+r2f :: Rational -> Double
+r2f = float2Double . realToFrac
diff --git a/compiler/GHC/StgToJS/Monad.hs b/compiler/GHC/StgToJS/Monad.hs
new file mode 100644
index 0000000000..b8deb36a63
--- /dev/null
+++ b/compiler/GHC/StgToJS/Monad.hs
@@ -0,0 +1,183 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+
+-- | JS codegen state monad
+module GHC.StgToJS.Monad
+ ( runG
+ , emitGlobal
+ , addDependency
+ , emitToplevel
+ , emitStatic
+ , emitClosureInfo
+ , emitForeign
+ , assertRtsStat
+ , getSettings
+ , globalOccs
+ , setGlobalIdCache
+ , getGlobalIdCache
+ , GlobalOcc(..)
+ -- * Group
+ , modifyGroup
+ , resetGroup
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Transform
+
+import GHC.StgToJS.Types
+
+import GHC.Unit.Module
+import GHC.Stg.Syntax
+
+import GHC.Types.SrcLoc
+import GHC.Types.Id
+import GHC.Types.Unique.FM
+import GHC.Types.ForeignCall
+
+import qualified Control.Monad.Trans.State.Strict as State
+import GHC.Data.FastString
+import GHC.Data.FastMutInt
+
+import qualified Data.Map as M
+import qualified Data.Set as S
+import qualified Data.List as L
+
+runG :: StgToJSConfig -> Module -> UniqFM Id CgStgExpr -> G a -> IO a
+runG config m unfloat action = State.evalStateT action =<< initState config m unfloat
+
+initState :: StgToJSConfig -> Module -> UniqFM Id CgStgExpr -> IO GenState
+initState config m unfloat = do
+ id_gen <- newFastMutInt 1
+ pure $ GenState
+ { gsSettings = config
+ , gsModule = m
+ , gsId = id_gen
+ , gsIdents = emptyIdCache
+ , gsUnfloated = unfloat
+ , gsGroup = defaultGenGroupState
+ , gsGlobal = []
+ }
+
+
+modifyGroup :: (GenGroupState -> GenGroupState) -> G ()
+modifyGroup f = State.modify mod_state
+ where
+ mod_state s = s { gsGroup = f (gsGroup s) }
+
+-- | emit a global (for the current module) toplevel statement
+emitGlobal :: JStat -> G ()
+emitGlobal stat = State.modify (\s -> s { gsGlobal = stat : gsGlobal s })
+
+-- | add a dependency on a particular symbol to the current group
+addDependency :: OtherSymb -> G ()
+addDependency symbol = modifyGroup mod_group
+ where
+ mod_group g = g { ggsExtraDeps = S.insert symbol (ggsExtraDeps g) }
+
+-- | emit a top-level statement for the current binding group
+emitToplevel :: JStat -> G ()
+emitToplevel s = modifyGroup mod_group
+ where
+ mod_group g = g { ggsToplevelStats = s : ggsToplevelStats g}
+
+-- | emit static data for the binding group
+emitStatic :: FastString -> StaticVal -> Maybe Ident -> G ()
+emitStatic ident val cc = modifyGroup mod_group
+ where
+ mod_group g = g { ggsStatic = mod_static (ggsStatic g) }
+ mod_static s = StaticInfo ident val cc : s
+
+-- | add closure info in our binding group. all heap objects must have closure info
+emitClosureInfo :: ClosureInfo -> G ()
+emitClosureInfo ci = modifyGroup mod_group
+ where
+ mod_group g = g { ggsClosureInfo = ci : ggsClosureInfo g}
+
+emitForeign :: Maybe RealSrcSpan
+ -> FastString
+ -> Safety
+ -> CCallConv
+ -> [FastString]
+ -> FastString
+ -> G ()
+emitForeign mbSpan pat safety cconv arg_tys res_ty = modifyGroup mod_group
+ where
+ mod_group g = g { ggsForeignRefs = new_ref : ggsForeignRefs g }
+ new_ref = ForeignJSRef spanTxt pat safety cconv arg_tys res_ty
+ spanTxt = case mbSpan of
+ -- TODO: Is there a better way to concatenate FastStrings?
+ Just sp -> mkFastString $
+ unpackFS (srcSpanFile sp) ++
+ " " ++
+ show (srcSpanStartLine sp, srcSpanStartCol sp) ++
+ "-" ++
+ show (srcSpanEndLine sp, srcSpanEndCol sp)
+ Nothing -> "<unknown>"
+
+
+
+
+
+
+-- | start with a new binding group
+resetGroup :: G ()
+resetGroup = State.modify (\s -> s { gsGroup = defaultGenGroupState })
+
+defaultGenGroupState :: GenGroupState
+defaultGenGroupState = GenGroupState [] [] [] [] 0 S.empty emptyGlobalIdCache []
+
+emptyGlobalIdCache :: GlobalIdCache
+emptyGlobalIdCache = GlobalIdCache emptyUFM
+
+emptyIdCache :: IdCache
+emptyIdCache = IdCache M.empty
+
+
+
+assertRtsStat :: G JStat -> G JStat
+assertRtsStat stat = do
+ s <- State.gets gsSettings
+ if csAssertRts s then stat else pure mempty
+
+getSettings :: G StgToJSConfig
+getSettings = State.gets gsSettings
+
+getGlobalIdCache :: G GlobalIdCache
+getGlobalIdCache = State.gets (ggsGlobalIdCache . gsGroup)
+
+setGlobalIdCache :: GlobalIdCache -> G ()
+setGlobalIdCache v = State.modify (\s -> s { gsGroup = (gsGroup s) { ggsGlobalIdCache = v}})
+
+
+data GlobalOcc = GlobalOcc
+ { global_ident :: !Ident
+ , global_id :: !Id
+ , global_count :: !Word
+ }
+
+-- | Return number of occurrences of every global id used in the given JStat.
+-- Sort by increasing occurrence count.
+globalOccs :: JStat -> G [GlobalOcc]
+globalOccs jst = do
+ GlobalIdCache gidc <- getGlobalIdCache
+ -- build a map form Ident Unique to (Ident, Id, Count)
+ let
+ cmp_cnt g1 g2 = compare (global_count g1) (global_count g2)
+ inc g1 g2 = g1 { global_count = global_count g1 + global_count g2 }
+ go gids = \case
+ [] -> -- return global Ids used locally sorted by increased use
+ L.sortBy cmp_cnt $ nonDetEltsUFM gids
+ (i:is) ->
+ -- check if the Id is global
+ case lookupUFM gidc i of
+ Nothing -> go gids is
+ Just (_k,gid) ->
+ -- add it to the list of already found global ids. Increasing
+ -- count by 1
+ let g = GlobalOcc i gid 1
+ in go (addToUFM_C inc gids i g) is
+
+ pure $ go emptyUFM (identsS jst)
diff --git a/compiler/GHC/StgToJS/Object.hs b/compiler/GHC/StgToJS/Object.hs
new file mode 100644
index 0000000000..f75d27e20b
--- /dev/null
+++ b/compiler/GHC/StgToJS/Object.hs
@@ -0,0 +1,622 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+-- only for DB.Binary instances on Module
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Object
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Sylvain Henry <sylvain.henry@iohk.io>
+-- Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Serialization/deserialization of binary .o files for the JavaScript backend
+-- The .o files contain dependency information and generated code.
+-- All strings are mapped to a central string table, which helps reduce
+-- file size and gives us efficient hash consing on read
+--
+-- Binary intermediate JavaScript object files:
+-- serialized [Text] -> ([ClosureInfo], JStat) blocks
+--
+-- file layout:
+-- - magic "GHCJSOBJ"
+-- - compiler version tag
+-- - module name
+-- - offsets of string table
+-- - dependencies
+-- - offset of the index
+-- - unit infos
+-- - index
+-- - string table
+--
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Object
+ ( putObject
+ , getObjectHeader
+ , getObjectBody
+ , getObject
+ , readObject
+ , getObjectUnits
+ , readObjectUnits
+ , readObjectDeps
+ , isGlobalUnit
+ , isJsObjectFile
+ , Object(..)
+ , IndexEntry(..)
+ , Deps (..), BlockDeps (..), DepsLocation (..)
+ , ExportedFun (..)
+ )
+where
+
+import GHC.Prelude
+
+import Control.Monad
+
+import Data.Array
+import Data.Int
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IS
+import Data.List (sortOn)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Word
+import Data.Char
+import Foreign.Storable
+import Foreign.Marshal.Array
+import System.IO
+
+import GHC.Settings.Constants (hiVersion)
+
+import GHC.JS.Syntax
+import GHC.StgToJS.Types
+
+import GHC.Unit.Module
+
+import GHC.Data.FastString
+
+import GHC.Types.Unique.Map
+import GHC.Float (castDoubleToWord64, castWord64ToDouble)
+
+import GHC.Utils.Binary hiding (SymbolTable)
+import GHC.Utils.Outputable (ppr, Outputable, hcat, vcat, text, hsep)
+import GHC.Utils.Monad (mapMaybeM)
+
+-- | An object file
+data Object = Object
+ { objModuleName :: !ModuleName
+ -- ^ name of the module
+ , objHandle :: !BinHandle
+ -- ^ BinHandle that can be used to read the ObjUnits
+ , objPayloadOffset :: !(Bin ObjUnit)
+ -- ^ Offset of the payload (units)
+ , objDeps :: !Deps
+ -- ^ Dependencies
+ , objIndex :: !Index
+ -- ^ The Index, serialed unit indices and their linkable units
+ }
+
+type BlockId = Int
+type BlockIds = IntSet
+
+-- | dependencies for a single module
+data Deps = Deps
+ { depsModule :: !Module
+ -- ^ module
+ , depsRequired :: !BlockIds
+ -- ^ blocks that always need to be linked when this object is loaded (e.g.
+ -- everything that contains initializer code or foreign exports)
+ , depsHaskellExported :: !(Map ExportedFun BlockId)
+ -- ^ exported Haskell functions -> block
+ , depsBlocks :: !(Array BlockId BlockDeps)
+ -- ^ info about each block
+ }
+
+instance Outputable Deps where
+ ppr d = vcat
+ [ hcat [ text "module: ", pprModule (depsModule d) ]
+ , hcat [ text "exports: ", ppr (M.keys (depsHaskellExported d)) ]
+ ]
+
+-- | Where are the dependencies
+data DepsLocation
+ = ObjectFile FilePath -- ^ In an object file at path
+ | ArchiveFile FilePath -- ^ In a Ar file at path
+ | InMemory String Object -- ^ In memory
+
+instance Outputable DepsLocation where
+ ppr = \case
+ ObjectFile fp -> hsep [text "ObjectFile", text fp]
+ ArchiveFile fp -> hsep [text "ArchiveFile", text fp]
+ InMemory s o -> hsep [text "InMemory", text s, ppr (objModuleName o)]
+
+data BlockDeps = BlockDeps
+ { blockBlockDeps :: [Int] -- ^ dependencies on blocks in this object
+ , blockFunDeps :: [ExportedFun] -- ^ dependencies on exported symbols in other objects
+ -- , blockForeignExported :: [ExpFun]
+ -- , blockForeignImported :: [ForeignRef]
+ }
+
+{- | we use the convention that the first unit (0) is a module-global
+ unit that's always included when something from the module
+ is loaded. everything in a module implicitly depends on the
+ global block. the global unit itself can't have dependencies
+ -}
+isGlobalUnit :: Int -> Bool
+isGlobalUnit n = n == 0
+
+-- | Exported Functions
+data ExportedFun = ExportedFun
+ { funModule :: !Module -- ^ The module containing the function
+ , funSymbol :: !LexicalFastString -- ^ The function
+ } deriving (Eq, Ord)
+
+instance Outputable ExportedFun where
+ ppr (ExportedFun m f) = vcat
+ [ hcat [ text "module: ", pprModule m ]
+ , hcat [ text "symbol: ", ppr f ]
+ ]
+
+-- | Write an ObjUnit, except for the top level symbols which are stored in the
+-- index
+putObjUnit :: BinHandle -> ObjUnit -> IO ()
+putObjUnit bh (ObjUnit _syms b c d e f g) = do
+ put_ bh b
+ put_ bh c
+ lazyPut bh d
+ put_ bh e
+ put_ bh f
+ put_ bh g
+
+-- | Read an ObjUnit and associate it to the given symbols (that must have been
+-- read from the index)
+getObjUnit :: [FastString] -> BinHandle -> IO ObjUnit
+getObjUnit syms bh = do
+ b <- get bh
+ c <- get bh
+ d <- lazyGet bh
+ e <- get bh
+ f <- get bh
+ g <- get bh
+ pure $ ObjUnit
+ { oiSymbols = syms
+ , oiClInfo = b
+ , oiStatic = c
+ , oiStat = d
+ , oiRaw = e
+ , oiFExports = f
+ , oiFImports = g
+ }
+
+
+-- | A tag that determines the kind of payload in the .o file. See
+-- @StgToJS.Linker.Arhive.magic@ for another kind of magic
+magic :: String
+magic = "GHCJSOBJ"
+
+-- | Serialized unit indexes and their exported symbols
+-- (the first unit is module-global)
+type Index = [IndexEntry]
+data IndexEntry = IndexEntry
+ { idxSymbols :: ![FastString] -- ^ Symbols exported by a unit
+ , idxOffset :: !(Bin ObjUnit) -- ^ Offset of the unit in the object file
+ }
+
+
+--------------------------------------------------------------------------------
+-- Essential oeprations on Objects
+--------------------------------------------------------------------------------
+
+-- | Given a handle to a Binary payload, add the module, 'mod_name', its
+-- dependencies, 'deps', and its linkable units to the payload.
+putObject
+ :: BinHandle
+ -> ModuleName -- ^ module
+ -> Deps -- ^ dependencies
+ -> [ObjUnit] -- ^ linkable units and their symbols
+ -> IO ()
+putObject bh mod_name deps os = do
+ forM_ magic (putByte bh . fromIntegral . ord)
+ put_ bh (show hiVersion)
+
+ -- we store the module name as a String because we don't want to have to
+ -- decode the FastString table just to decode it when we're looking for an
+ -- object in an archive.
+ put_ bh (moduleNameString mod_name)
+
+ (bh_fs, _bin_dict, put_dict) <- initFSTable bh
+
+ forwardPut_ bh (const put_dict) $ do
+ put_ bh_fs deps
+
+ -- forward put the index
+ forwardPut_ bh_fs (put_ bh_fs) $ do
+ idx <- forM os $ \o -> do
+ p <- tellBin bh_fs
+ -- write units without their symbols
+ putObjUnit bh_fs o
+ -- return symbols and offset to store in the index
+ pure (oiSymbols o,p)
+ pure idx
+
+-- | Test if the object file is a JS object
+isJsObjectFile :: FilePath -> IO Bool
+isJsObjectFile fp = do
+ let !n = length magic
+ withBinaryFile fp ReadMode $ \hdl -> do
+ allocaArray n $ \ptr -> do
+ n' <- hGetBuf hdl ptr n
+ if (n' /= n)
+ then pure False
+ else checkMagic (peekElemOff ptr)
+
+-- | Check magic
+checkMagic :: (Int -> IO Word8) -> IO Bool
+checkMagic get_byte = do
+ let go_magic !i = \case
+ [] -> pure True
+ (e:es) -> get_byte i >>= \case
+ c | fromIntegral (ord e) == c -> go_magic (i+1) es
+ | otherwise -> pure False
+ go_magic 0 magic
+
+-- | Parse object magic
+getCheckMagic :: BinHandle -> IO Bool
+getCheckMagic bh = checkMagic (const (getByte bh))
+
+-- | Parse object header
+getObjectHeader :: BinHandle -> IO (Either String ModuleName)
+getObjectHeader bh = do
+ is_magic <- getCheckMagic bh
+ case is_magic of
+ False -> pure (Left "invalid magic header")
+ True -> do
+ is_correct_version <- ((== hiVersion) . read) <$> get bh
+ case is_correct_version of
+ False -> pure (Left "invalid header version")
+ True -> do
+ mod_name <- get bh
+ pure (Right (mkModuleName (mod_name)))
+
+
+-- | Parse object body. Must be called after a sucessful getObjectHeader
+getObjectBody :: BinHandle -> ModuleName -> IO Object
+getObjectBody bh0 mod_name = do
+ -- Read the string table
+ dict <- forwardGet bh0 (getDictionary bh0)
+ let bh = setUserData bh0 $ noUserData { ud_get_fs = getDictFastString dict }
+
+ deps <- get bh
+ idx <- forwardGet bh (get bh)
+ payload_pos <- tellBin bh
+
+ pure $ Object
+ { objModuleName = mod_name
+ , objHandle = bh
+ , objPayloadOffset = payload_pos
+ , objDeps = deps
+ , objIndex = idx
+ }
+
+-- | Parse object
+getObject :: BinHandle -> IO (Maybe Object)
+getObject bh = do
+ getObjectHeader bh >>= \case
+ Left _err -> pure Nothing
+ Right mod_name -> Just <$> getObjectBody bh mod_name
+
+-- | Read object from file
+--
+-- The object is still in memory after this (see objHandle).
+readObject :: FilePath -> IO (Maybe Object)
+readObject file = do
+ bh <- readBinMem file
+ getObject bh
+
+-- | Reads only the part necessary to get the dependencies
+readObjectDeps :: FilePath -> IO (Maybe Deps)
+readObjectDeps file = do
+ bh <- readBinMem file
+ getObject bh >>= \case
+ Just obj -> pure $! Just $! objDeps obj
+ Nothing -> pure Nothing
+
+-- | Get units in the object file, using the given filtering function
+getObjectUnits :: Object -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]
+getObjectUnits obj pred = mapMaybeM read_entry (zip (objIndex obj) [0..])
+ where
+ bh = objHandle obj
+ read_entry (e@(IndexEntry syms offset),i)
+ | pred i e = do
+ seekBin bh offset
+ Just <$> getObjUnit syms bh
+ | otherwise = pure Nothing
+
+-- | Read units in the object file, using the given filtering function
+readObjectUnits :: FilePath -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]
+readObjectUnits file pred = do
+ readObject file >>= \case
+ Nothing -> pure []
+ Just obj -> getObjectUnits obj pred
+
+
+--------------------------------------------------------------------------------
+-- Helper functions
+--------------------------------------------------------------------------------
+
+putEnum :: Enum a => BinHandle -> a -> IO ()
+putEnum bh x | n > 65535 = error ("putEnum: out of range: " ++ show n)
+ | otherwise = put_ bh n
+ where n = fromIntegral $ fromEnum x :: Word16
+
+getEnum :: Enum a => BinHandle -> IO a
+getEnum bh = toEnum . fromIntegral <$> (get bh :: IO Word16)
+
+-- | Helper to convert Int to Int32
+toI32 :: Int -> Int32
+toI32 = fromIntegral
+
+-- | Helper to convert Int32 to Int
+fromI32 :: Int32 -> Int
+fromI32 = fromIntegral
+
+
+--------------------------------------------------------------------------------
+-- Binary Instances
+--------------------------------------------------------------------------------
+
+instance Binary IndexEntry where
+ put_ bh (IndexEntry a b) = put_ bh a >> put_ bh b
+ get bh = IndexEntry <$> get bh <*> get bh
+
+instance Binary Deps where
+ put_ bh (Deps m r e b) = do
+ put_ bh m
+ put_ bh (map toI32 $ IS.toList r)
+ put_ bh (map (\(x,y) -> (x, toI32 y)) $ M.toList e)
+ put_ bh (elems b)
+ get bh = Deps <$> get bh
+ <*> (IS.fromList . map fromI32 <$> get bh)
+ <*> (M.fromList . map (\(x,y) -> (x, fromI32 y)) <$> get bh)
+ <*> ((\xs -> listArray (0, length xs - 1) xs) <$> get bh)
+
+instance Binary BlockDeps where
+ put_ bh (BlockDeps bbd bfd) = put_ bh bbd >> put_ bh bfd
+ get bh = BlockDeps <$> get bh <*> get bh
+
+instance Binary ForeignJSRef where
+ put_ bh (ForeignJSRef span pat safety cconv arg_tys res_ty) =
+ put_ bh span >> put_ bh pat >> putEnum bh safety >> putEnum bh cconv >> put_ bh arg_tys >> put_ bh res_ty
+ get bh = ForeignJSRef <$> get bh <*> get bh <*> getEnum bh <*> getEnum bh <*> get bh <*> get bh
+
+instance Binary ExpFun where
+ put_ bh (ExpFun isIO args res) = put_ bh isIO >> put_ bh args >> put_ bh res
+ get bh = ExpFun <$> get bh <*> get bh <*> get bh
+
+instance Binary JStat where
+ put_ bh (DeclStat i e) = putByte bh 1 >> put_ bh i >> put_ bh e
+ put_ bh (ReturnStat e) = putByte bh 2 >> put_ bh e
+ put_ bh (IfStat e s1 s2) = putByte bh 3 >> put_ bh e >> put_ bh s1 >> put_ bh s2
+ put_ bh (WhileStat b e s) = putByte bh 4 >> put_ bh b >> put_ bh e >> put_ bh s
+ put_ bh (ForInStat b i e s) = putByte bh 5 >> put_ bh b >> put_ bh i >> put_ bh e >> put_ bh s
+ put_ bh (SwitchStat e ss s) = putByte bh 6 >> put_ bh e >> put_ bh ss >> put_ bh s
+ put_ bh (TryStat s1 i s2 s3) = putByte bh 7 >> put_ bh s1 >> put_ bh i >> put_ bh s2 >> put_ bh s3
+ put_ bh (BlockStat xs) = putByte bh 8 >> put_ bh xs
+ put_ bh (ApplStat e es) = putByte bh 9 >> put_ bh e >> put_ bh es
+ put_ bh (UOpStat o e) = putByte bh 10 >> put_ bh o >> put_ bh e
+ put_ bh (AssignStat e1 e2) = putByte bh 11 >> put_ bh e1 >> put_ bh e2
+ put_ _ (UnsatBlock {}) = error "put_ bh JStat: UnsatBlock"
+ put_ bh (LabelStat l s) = putByte bh 12 >> put_ bh l >> put_ bh s
+ put_ bh (BreakStat ml) = putByte bh 13 >> put_ bh ml
+ put_ bh (ContinueStat ml) = putByte bh 14 >> put_ bh ml
+ get bh = getByte bh >>= \case
+ 1 -> DeclStat <$> get bh <*> get bh
+ 2 -> ReturnStat <$> get bh
+ 3 -> IfStat <$> get bh <*> get bh <*> get bh
+ 4 -> WhileStat <$> get bh <*> get bh <*> get bh
+ 5 -> ForInStat <$> get bh <*> get bh <*> get bh <*> get bh
+ 6 -> SwitchStat <$> get bh <*> get bh <*> get bh
+ 7 -> TryStat <$> get bh <*> get bh <*> get bh <*> get bh
+ 8 -> BlockStat <$> get bh
+ 9 -> ApplStat <$> get bh <*> get bh
+ 10 -> UOpStat <$> get bh <*> get bh
+ 11 -> AssignStat <$> get bh <*> get bh
+ 12 -> LabelStat <$> get bh <*> get bh
+ 13 -> BreakStat <$> get bh
+ 14 -> ContinueStat <$> get bh
+ n -> error ("Binary get bh JStat: invalid tag: " ++ show n)
+
+instance Binary JExpr where
+ put_ bh (ValExpr v) = putByte bh 1 >> put_ bh v
+ put_ bh (SelExpr e i) = putByte bh 2 >> put_ bh e >> put_ bh i
+ put_ bh (IdxExpr e1 e2) = putByte bh 3 >> put_ bh e1 >> put_ bh e2
+ put_ bh (InfixExpr o e1 e2) = putByte bh 4 >> put_ bh o >> put_ bh e1 >> put_ bh e2
+ put_ bh (UOpExpr o e) = putByte bh 5 >> put_ bh o >> put_ bh e
+ put_ bh (IfExpr e1 e2 e3) = putByte bh 6 >> put_ bh e1 >> put_ bh e2 >> put_ bh e3
+ put_ bh (ApplExpr e es) = putByte bh 7 >> put_ bh e >> put_ bh es
+ put_ _ (UnsatExpr {}) = error "put_ bh JExpr: UnsatExpr"
+ get bh = getByte bh >>= \case
+ 1 -> ValExpr <$> get bh
+ 2 -> SelExpr <$> get bh <*> get bh
+ 3 -> IdxExpr <$> get bh <*> get bh
+ 4 -> InfixExpr <$> get bh <*> get bh <*> get bh
+ 5 -> UOpExpr <$> get bh <*> get bh
+ 6 -> IfExpr <$> get bh <*> get bh <*> get bh
+ 7 -> ApplExpr <$> get bh <*> get bh
+ n -> error ("Binary get bh JExpr: invalid tag: " ++ show n)
+
+instance Binary JVal where
+ put_ bh (JVar i) = putByte bh 1 >> put_ bh i
+ put_ bh (JList es) = putByte bh 2 >> put_ bh es
+ put_ bh (JDouble d) = putByte bh 3 >> put_ bh d
+ put_ bh (JInt i) = putByte bh 4 >> put_ bh i
+ put_ bh (JStr xs) = putByte bh 5 >> put_ bh xs
+ put_ bh (JRegEx xs) = putByte bh 6 >> put_ bh xs
+ put_ bh (JHash m) = putByte bh 7 >> put_ bh (sortOn (LexicalFastString . fst) $ nonDetEltsUniqMap m)
+ put_ bh (JFunc is s) = putByte bh 8 >> put_ bh is >> put_ bh s
+ put_ _ (UnsatVal {}) = error "put_ bh JVal: UnsatVal"
+ get bh = getByte bh >>= \case
+ 1 -> JVar <$> get bh
+ 2 -> JList <$> get bh
+ 3 -> JDouble <$> get bh
+ 4 -> JInt <$> get bh
+ 5 -> JStr <$> get bh
+ 6 -> JRegEx <$> get bh
+ 7 -> JHash . listToUniqMap <$> get bh
+ 8 -> JFunc <$> get bh <*> get bh
+ n -> error ("Binary get bh JVal: invalid tag: " ++ show n)
+
+instance Binary Ident where
+ put_ bh (TxtI xs) = put_ bh xs
+ get bh = TxtI <$> get bh
+
+-- we need to preserve NaN and infinities, unfortunately the Binary instance for Double does not do this
+instance Binary SaneDouble where
+ put_ bh (SaneDouble d)
+ | isNaN d = putByte bh 1
+ | isInfinite d && d > 0 = putByte bh 2
+ | isInfinite d && d < 0 = putByte bh 3
+ | isNegativeZero d = putByte bh 4
+ | otherwise = putByte bh 5 >> put_ bh (castDoubleToWord64 d)
+ get bh = getByte bh >>= \case
+ 1 -> pure $ SaneDouble (0 / 0)
+ 2 -> pure $ SaneDouble (1 / 0)
+ 3 -> pure $ SaneDouble ((-1) / 0)
+ 4 -> pure $ SaneDouble (-0)
+ 5 -> SaneDouble . castWord64ToDouble <$> get bh
+ n -> error ("Binary get bh SaneDouble: invalid tag: " ++ show n)
+
+instance Binary ClosureInfo where
+ put_ bh (ClosureInfo v regs name layo typ static) = do
+ put_ bh v >> put_ bh regs >> put_ bh name >> put_ bh layo >> put_ bh typ >> put_ bh static
+ get bh = ClosureInfo <$> get bh <*> get bh <*> get bh <*> get bh <*> get bh <*> get bh
+
+instance Binary JSFFIType where
+ put_ bh = putEnum bh
+ get bh = getEnum bh
+
+instance Binary VarType where
+ put_ bh = putEnum bh
+ get bh = getEnum bh
+
+instance Binary CIRegs where
+ put_ bh CIRegsUnknown = putByte bh 1
+ put_ bh (CIRegs skip types) = putByte bh 2 >> put_ bh skip >> put_ bh types
+ get bh = getByte bh >>= \case
+ 1 -> pure CIRegsUnknown
+ 2 -> CIRegs <$> get bh <*> get bh
+ n -> error ("Binary get bh CIRegs: invalid tag: " ++ show n)
+
+instance Binary JOp where
+ put_ bh = putEnum bh
+ get bh = getEnum bh
+
+instance Binary JUOp where
+ put_ bh = putEnum bh
+ get bh = getEnum bh
+
+-- 16 bit sizes should be enough...
+instance Binary CILayout where
+ put_ bh CILayoutVariable = putByte bh 1
+ put_ bh (CILayoutUnknown size) = putByte bh 2 >> put_ bh size
+ put_ bh (CILayoutFixed size types) = putByte bh 3 >> put_ bh size >> put_ bh types
+ get bh = getByte bh >>= \case
+ 1 -> pure CILayoutVariable
+ 2 -> CILayoutUnknown <$> get bh
+ 3 -> CILayoutFixed <$> get bh <*> get bh
+ n -> error ("Binary get bh CILayout: invalid tag: " ++ show n)
+
+instance Binary CIStatic where
+ put_ bh (CIStaticRefs refs) = putByte bh 1 >> put_ bh refs
+ get bh = getByte bh >>= \case
+ 1 -> CIStaticRefs <$> get bh
+ n -> error ("Binary get bh CIStatic: invalid tag: " ++ show n)
+
+instance Binary CIType where
+ put_ bh (CIFun arity regs) = putByte bh 1 >> put_ bh arity >> put_ bh regs
+ put_ bh CIThunk = putByte bh 2
+ put_ bh (CICon conTag) = putByte bh 3 >> put_ bh conTag
+ put_ bh CIPap = putByte bh 4
+ put_ bh CIBlackhole = putByte bh 5
+ put_ bh CIStackFrame = putByte bh 6
+ get bh = getByte bh >>= \case
+ 1 -> CIFun <$> get bh <*> get bh
+ 2 -> pure CIThunk
+ 3 -> CICon <$> get bh
+ 4 -> pure CIPap
+ 5 -> pure CIBlackhole
+ 6 -> pure CIStackFrame
+ n -> error ("Binary get bh CIType: invalid tag: " ++ show n)
+
+instance Binary ExportedFun where
+ put_ bh (ExportedFun modu symb) = put_ bh modu >> put_ bh symb
+ get bh = ExportedFun <$> get bh <*> get bh
+
+instance Binary StaticInfo where
+ put_ bh (StaticInfo ident val cc) = put_ bh ident >> put_ bh val >> put_ bh cc
+ get bh = StaticInfo <$> get bh <*> get bh <*> get bh
+
+instance Binary StaticVal where
+ put_ bh (StaticFun f args) = putByte bh 1 >> put_ bh f >> put_ bh args
+ put_ bh (StaticThunk t) = putByte bh 2 >> put_ bh t
+ put_ bh (StaticUnboxed u) = putByte bh 3 >> put_ bh u
+ put_ bh (StaticData dc args) = putByte bh 4 >> put_ bh dc >> put_ bh args
+ put_ bh (StaticList xs t) = putByte bh 5 >> put_ bh xs >> put_ bh t
+ get bh = getByte bh >>= \case
+ 1 -> StaticFun <$> get bh <*> get bh
+ 2 -> StaticThunk <$> get bh
+ 3 -> StaticUnboxed <$> get bh
+ 4 -> StaticData <$> get bh <*> get bh
+ 5 -> StaticList <$> get bh <*> get bh
+ n -> error ("Binary get bh StaticVal: invalid tag " ++ show n)
+
+instance Binary StaticUnboxed where
+ put_ bh (StaticUnboxedBool b) = putByte bh 1 >> put_ bh b
+ put_ bh (StaticUnboxedInt i) = putByte bh 2 >> put_ bh i
+ put_ bh (StaticUnboxedDouble d) = putByte bh 3 >> put_ bh d
+ put_ bh (StaticUnboxedString str) = putByte bh 4 >> put_ bh str
+ put_ bh (StaticUnboxedStringOffset str) = putByte bh 5 >> put_ bh str
+ get bh = getByte bh >>= \case
+ 1 -> StaticUnboxedBool <$> get bh
+ 2 -> StaticUnboxedInt <$> get bh
+ 3 -> StaticUnboxedDouble <$> get bh
+ 4 -> StaticUnboxedString <$> get bh
+ 5 -> StaticUnboxedStringOffset <$> get bh
+ n -> error ("Binary get bh StaticUnboxed: invalid tag " ++ show n)
+
+instance Binary StaticArg where
+ put_ bh (StaticObjArg i) = putByte bh 1 >> put_ bh i
+ put_ bh (StaticLitArg p) = putByte bh 2 >> put_ bh p
+ put_ bh (StaticConArg c args) = putByte bh 3 >> put_ bh c >> put_ bh args
+ get bh = getByte bh >>= \case
+ 1 -> StaticObjArg <$> get bh
+ 2 -> StaticLitArg <$> get bh
+ 3 -> StaticConArg <$> get bh <*> get bh
+ n -> error ("Binary get bh StaticArg: invalid tag " ++ show n)
+
+instance Binary StaticLit where
+ put_ bh (BoolLit b) = putByte bh 1 >> put_ bh b
+ put_ bh (IntLit i) = putByte bh 2 >> put_ bh i
+ put_ bh NullLit = putByte bh 3
+ put_ bh (DoubleLit d) = putByte bh 4 >> put_ bh d
+ put_ bh (StringLit t) = putByte bh 5 >> put_ bh t
+ put_ bh (BinLit b) = putByte bh 6 >> put_ bh b
+ put_ bh (LabelLit b t) = putByte bh 7 >> put_ bh b >> put_ bh t
+ get bh = getByte bh >>= \case
+ 1 -> BoolLit <$> get bh
+ 2 -> IntLit <$> get bh
+ 3 -> pure NullLit
+ 4 -> DoubleLit <$> get bh
+ 5 -> StringLit <$> get bh
+ 6 -> BinLit <$> get bh
+ 7 -> LabelLit <$> get bh <*> get bh
+ n -> error ("Binary get bh StaticLit: invalid tag " ++ show n)
diff --git a/compiler/GHC/StgToJS/Prim.hs b/compiler/GHC/StgToJS/Prim.hs
new file mode 100644
index 0000000000..6085b110cf
--- /dev/null
+++ b/compiler/GHC/StgToJS/Prim.hs
@@ -0,0 +1,1509 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE MultiWayIf #-}
+
+-- disable this warning because of all the lambdas matching on primops'
+-- arguments.
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+
+module GHC.StgToJS.Prim
+ ( genPrim
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax hiding (JUOp (..))
+import GHC.JS.Make
+
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Types
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Regs
+
+import GHC.Core.Type
+
+import GHC.Builtin.PrimOps
+import GHC.Tc.Utils.TcType (isBoolTy)
+import GHC.Utils.Encoding (zEncodeString)
+
+import GHC.Data.FastString
+import GHC.Utils.Outputable (renderWithContext, defaultSDocContext, ppr)
+import Data.Maybe
+
+
+genPrim :: Bool -- ^ Profiling (cost-centres) enabled
+ -> Bool -- ^ Array bounds-checking enabled
+ -> Type
+ -> PrimOp -- ^ the primitive operation
+ -> [JExpr] -- ^ where to store the result
+ -> [JExpr] -- ^ arguments
+ -> PrimRes
+genPrim prof bound ty op = case op of
+ CharGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ CharGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ CharEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ CharNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+ CharLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ CharLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ OrdOp -> \[r] [x] -> PrimInline $ r |= x
+
+ Int8ToWord8Op -> \[r] [x] -> PrimInline $ r |= mask8 x
+ Word8ToInt8Op -> \[r] [x] -> PrimInline $ r |= signExtend8 x
+ Int16ToWord16Op -> \[r] [x] -> PrimInline $ r |= mask16 x
+ Word16ToInt16Op -> \[r] [x] -> PrimInline $ r |= signExtend16 x
+ Int32ToWord32Op -> \[r] [x] -> PrimInline $ r |= x .>>>. zero_
+ Word32ToInt32Op -> \[r] [x] -> PrimInline $ r |= toI32 x
+
+------------------------------ Int ----------------------------------------------
+
+ IntAddOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Add x y)
+ IntSubOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Sub x y)
+ IntMulOp -> \[r] [x,y] -> PrimInline $ r |= app "h$mulInt32" [x, y]
+ IntMul2Op -> \[c,hr,lr] [x,y] -> PrimInline $ appT [c,hr,lr] "h$hs_timesInt2" [x, y]
+ IntMulMayOfloOp -> \[r] [x,y] -> PrimInline $ jVar \tmp -> mconcat
+ [ tmp |= Mul x y
+ , r |= if01 (tmp .===. toI32 tmp)
+ ]
+ IntQuotOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Div x y)
+ IntRemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y
+ IntQuotRemOp -> \[q,r] [x,y] -> PrimInline $ mconcat
+ [ q |= toI32 (Div x y)
+ , r |= x `Sub` (Mul y q)
+ ]
+ IntAndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y
+ IntOrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y
+ IntXorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y
+ IntNotOp -> \[r] [x] -> PrimInline $ r |= BNot x
+
+ IntNegOp -> \[r] [x] -> PrimInline $ r |= toI32 (Negate x)
+-- add with carry: overflow == 0 iff no overflow
+ IntAddCOp -> \[r,overf] [x,y] ->
+ PrimInline $ jVar \rt -> mconcat
+ [ rt |= Add x y
+ , r |= toI32 rt
+ , overf |= if10 (r .!=. rt)
+ ]
+ IntSubCOp -> \[r,overf] [x,y] ->
+ PrimInline $ jVar \rt -> mconcat
+ [ rt |= Sub x y
+ , r |= toI32 rt
+ , overf |= if10 (r .!=. rt)
+ ]
+ IntGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ IntGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ IntEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ IntNeOp -> \[r] [x,y] -> PrimInline $ r |= if10(x .!==. y)
+ IntLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ IntLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ ChrOp -> \[r] [x] -> PrimInline $ r |= x
+ IntToWordOp -> \[r] [x] -> PrimInline $ r |= x .>>>. 0
+ IntToFloatOp -> \[r] [x] -> PrimInline $ r |= x
+ IntToDoubleOp -> \[r] [x] -> PrimInline $ r |= x
+ IntSllOp -> \[r] [x,y] -> PrimInline $ r |= x .<<. y
+ IntSraOp -> \[r] [x,y] -> PrimInline $ r |= x .>>. y
+ IntSrlOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (x .>>>. y)
+
+------------------------------ Int8 ---------------------------------------------
+
+ Int8ToIntOp -> \[r] [x] -> PrimInline $ r |= x
+ IntToInt8Op -> \[r] [x] -> PrimInline $ r |= signExtend8 x
+ Int8NegOp -> \[r] [x] -> PrimInline $ r |= signExtend8 (Negate x)
+ Int8AddOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Add x y)
+ Int8SubOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Sub x y)
+ Int8MulOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Mul x y)
+ Int8QuotOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (quotShortInt 8 x y)
+ Int8RemOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (remShortInt 8 x y)
+ Int8QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat
+ [ r1 |= signExtend8 (quotShortInt 8 x y)
+ , r2 |= signExtend8 (remShortInt 8 x y)
+ ]
+ Int8EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ Int8GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .>=. (y .<<. (Int 24)))
+ Int8GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .>. (y .<<. (Int 24)))
+ Int8LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .<=. (y .<<. (Int 24)))
+ Int8LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .<. (y .<<. (Int 24)))
+ Int8NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+
+ Int8SraOp -> \[r] [x,i] -> PrimInline $ r |= x .>>. i
+ Int8SrlOp -> \[r] [x,i] -> PrimInline $ r |= signExtend8 (mask8 x .>>>. i)
+ Int8SllOp -> \[r] [x,i] -> PrimInline $ r |= signExtend8 (mask8 (x .<<. i))
+
+------------------------------ Word8 --------------------------------------------
+
+ Word8ToWordOp -> \[r] [x] -> PrimInline $ r |= mask8 x
+ WordToWord8Op -> \[r] [x] -> PrimInline $ r |= mask8 x
+
+ Word8AddOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Add x y)
+ Word8SubOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Sub x y)
+ Word8MulOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Mul x y)
+ Word8QuotOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Div x y)
+ Word8RemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y
+ Word8QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat
+ [ r1 |= toI32 (Div x y)
+ , r2 |= Mod x y
+ ]
+ Word8EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ Word8GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ Word8GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ Word8LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ Word8LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ Word8NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+
+ Word8AndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y
+ Word8OrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y
+ Word8XorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y
+ Word8NotOp -> \[r] [x] -> PrimInline $ r |= BXor x (Int 0xff)
+
+ Word8SllOp -> \[r] [x,i] -> PrimInline $ r |= mask8 (x .<<. i)
+ Word8SrlOp -> \[r] [x,i] -> PrimInline $ r |= x .>>>. i
+
+------------------------------ Int16 -------------------------------------------
+
+ Int16ToIntOp -> \[r] [x] -> PrimInline $ r |= x
+ IntToInt16Op -> \[r] [x] -> PrimInline $ r |= signExtend16 x
+
+ Int16NegOp -> \[r] [x] -> PrimInline $ r |= signExtend16 (Negate x)
+ Int16AddOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Add x y)
+ Int16SubOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Sub x y)
+ Int16MulOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Mul x y)
+ Int16QuotOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (quotShortInt 16 x y)
+ Int16RemOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (remShortInt 16 x y)
+ Int16QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat
+ [ r1 |= signExtend16 (quotShortInt 16 x y)
+ , r2 |= signExtend16 (remShortInt 16 x y)
+ ]
+ Int16EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ Int16GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .>=. (y .<<. (Int 16)))
+ Int16GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .>. (y .<<. (Int 16)))
+ Int16LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .<=. (y .<<. (Int 16)))
+ Int16LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .<. (y .<<. (Int 16)))
+ Int16NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+
+ Int16SraOp -> \[r] [x,i] -> PrimInline $ r |= x .>>. i
+ Int16SrlOp -> \[r] [x,i] -> PrimInline $ r |= signExtend16 (mask16 x .>>>. i)
+ Int16SllOp -> \[r] [x,i] -> PrimInline $ r |= signExtend16 (x .<<. i)
+
+------------------------------ Word16 ------------------------------------------
+
+ Word16ToWordOp -> \[r] [x] -> PrimInline $ r |= x
+ WordToWord16Op -> \[r] [x] -> PrimInline $ r |= mask16 x
+
+ Word16AddOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Add x y)
+ Word16SubOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Sub x y)
+ Word16MulOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Mul x y)
+ Word16QuotOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Div x y)
+ Word16RemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y
+ Word16QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat
+ [ r1 |= toI32 (Div x y)
+ , r2 |= Mod x y
+ ]
+ Word16EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ Word16GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ Word16GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ Word16LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ Word16LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ Word16NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+
+ Word16AndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y
+ Word16OrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y
+ Word16XorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y
+ Word16NotOp -> \[r] [x] -> PrimInline $ r |= BXor x (Int 0xffff)
+
+ Word16SllOp -> \[r] [x,i] -> PrimInline $ r |= mask16 (x .<<. i)
+ Word16SrlOp -> \[r] [x,i] -> PrimInline $ r |= x .>>>. i
+
+------------------------------ Int32 --------------------------------------------
+
+ Int32ToIntOp -> \[r] [x] -> PrimInline $ r |= x
+ IntToInt32Op -> \[r] [x] -> PrimInline $ r |= x
+
+ Int32NegOp -> \rs xs -> genPrim prof bound ty IntNegOp rs xs
+ Int32AddOp -> \rs xs -> genPrim prof bound ty IntAddOp rs xs
+ Int32SubOp -> \rs xs -> genPrim prof bound ty IntSubOp rs xs
+ Int32MulOp -> \rs xs -> genPrim prof bound ty IntMulOp rs xs
+ Int32QuotOp -> \rs xs -> genPrim prof bound ty IntQuotOp rs xs
+ Int32RemOp -> \rs xs -> genPrim prof bound ty IntRemOp rs xs
+ Int32QuotRemOp -> \rs xs -> genPrim prof bound ty IntQuotRemOp rs xs
+
+ Int32EqOp -> \rs xs -> genPrim prof bound ty IntEqOp rs xs
+ Int32GeOp -> \rs xs -> genPrim prof bound ty IntGeOp rs xs
+ Int32GtOp -> \rs xs -> genPrim prof bound ty IntGtOp rs xs
+ Int32LeOp -> \rs xs -> genPrim prof bound ty IntLeOp rs xs
+ Int32LtOp -> \rs xs -> genPrim prof bound ty IntLtOp rs xs
+ Int32NeOp -> \rs xs -> genPrim prof bound ty IntNeOp rs xs
+
+ Int32SraOp -> \rs xs -> genPrim prof bound ty IntSraOp rs xs
+ Int32SrlOp -> \rs xs -> genPrim prof bound ty IntSrlOp rs xs
+ Int32SllOp -> \rs xs -> genPrim prof bound ty IntSllOp rs xs
+
+------------------------------ Word32 -------------------------------------------
+
+ Word32ToWordOp -> \[r] [x] -> PrimInline $ r |= x
+ WordToWord32Op -> \[r] [x] -> PrimInline $ r |= x
+
+ Word32AddOp -> \rs xs -> genPrim prof bound ty WordAddOp rs xs
+ Word32SubOp -> \rs xs -> genPrim prof bound ty WordSubOp rs xs
+ Word32MulOp -> \rs xs -> genPrim prof bound ty WordMulOp rs xs
+ Word32QuotOp -> \rs xs -> genPrim prof bound ty WordQuotOp rs xs
+ Word32RemOp -> \rs xs -> genPrim prof bound ty WordRemOp rs xs
+ Word32QuotRemOp -> \rs xs -> genPrim prof bound ty WordQuotRemOp rs xs
+
+ Word32EqOp -> \rs xs -> genPrim prof bound ty WordEqOp rs xs
+ Word32GeOp -> \rs xs -> genPrim prof bound ty WordGeOp rs xs
+ Word32GtOp -> \rs xs -> genPrim prof bound ty WordGtOp rs xs
+ Word32LeOp -> \rs xs -> genPrim prof bound ty WordLeOp rs xs
+ Word32LtOp -> \rs xs -> genPrim prof bound ty WordLtOp rs xs
+ Word32NeOp -> \rs xs -> genPrim prof bound ty WordNeOp rs xs
+
+ Word32AndOp -> \rs xs -> genPrim prof bound ty WordAndOp rs xs
+ Word32OrOp -> \rs xs -> genPrim prof bound ty WordOrOp rs xs
+ Word32XorOp -> \rs xs -> genPrim prof bound ty WordXorOp rs xs
+ Word32NotOp -> \rs xs -> genPrim prof bound ty WordNotOp rs xs
+
+ Word32SllOp -> \rs xs -> genPrim prof bound ty WordSllOp rs xs
+ Word32SrlOp -> \rs xs -> genPrim prof bound ty WordSrlOp rs xs
+
+------------------------------ Int64 --------------------------------------------
+
+ Int64ToIntOp -> \[r] [_h,l] -> PrimInline $ r |= toI32 l
+
+ Int64NegOp -> \[r_h,r_l] [h,l] ->
+ PrimInline $ mconcat
+ [ r_l |= toU32 (BNot l + 1)
+ , r_h |= toI32 (BNot h + Not r_l)
+ ]
+
+ Int64AddOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_plusInt64" [h0,l0,h1,l1]
+ Int64SubOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_minusInt64" [h0,l0,h1,l1]
+ Int64MulOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_timesInt64" [h0,l0,h1,l1]
+ Int64QuotOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_quotInt64" [h0,l0,h1,l1]
+ Int64RemOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_remInt64" [h0,l0,h1,l1]
+
+ Int64SllOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftLLInt64" [h,l,n]
+ Int64SraOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRAInt64" [h,l,n]
+ Int64SrlOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRLInt64" [h,l,n]
+
+ Int64ToWord64Op -> \[r1,r2] [x1,x2] ->
+ PrimInline $ mconcat
+ [ r1 |= toU32 x1
+ , r2 |= x2
+ ]
+ IntToInt64Op -> \[r1,r2] [x] ->
+ PrimInline $ mconcat
+ [ r1 |= if_ (x .<. 0) (-1) 0 -- sign-extension
+ , r2 |= toU32 x
+ ]
+
+ Int64EqOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LAnd (l0 .===. l1) (h0 .===. h1))
+ Int64NeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (l0 .!==. l1) (h0 .!==. h1))
+ Int64GeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>=. l1)))
+ Int64GtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>. l1)))
+ Int64LeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<=. l1)))
+ Int64LtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<. l1)))
+
+------------------------------ Word64 -------------------------------------------
+
+ Word64ToWordOp -> \[r] [_x1,x2] -> PrimInline $ r |= x2
+
+ WordToWord64Op -> \[rh,rl] [x] ->
+ PrimInline $ mconcat
+ [ rh |= 0
+ , rl |= x
+ ]
+
+ Word64ToInt64Op -> \[r1,r2] [x1,x2] ->
+ PrimInline $ mconcat
+ [ r1 |= toI32 x1
+ , r2 |= x2
+ ]
+
+ Word64EqOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LAnd (l0 .===. l1) (h0 .===. h1))
+ Word64NeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (l0 .!==. l1) (h0 .!==. h1))
+ Word64GeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>=. l1)))
+ Word64GtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>. l1)))
+ Word64LeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<=. l1)))
+ Word64LtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<. l1)))
+
+ Word64SllOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftLWord64" [h,l,n]
+ Word64SrlOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRWord64" [h,l,n]
+
+ Word64OrOp -> \[hr,hl] [h0, l0, h1, l1] ->
+ PrimInline $ mconcat
+ [ hr |= toU32 (BOr h0 h1)
+ , hl |= toU32 (BOr l0 l1)
+ ]
+
+ Word64AndOp -> \[hr,hl] [h0, l0, h1, l1] ->
+ PrimInline $ mconcat
+ [ hr |= toU32 (BAnd h0 h1)
+ , hl |= toU32 (BAnd l0 l1)
+ ]
+
+ Word64XorOp -> \[hr,hl] [h0, l0, h1, l1] ->
+ PrimInline $ mconcat
+ [ hr |= toU32 (BXor h0 h1)
+ , hl |= toU32 (BXor l0 l1)
+ ]
+
+ Word64NotOp -> \[hr,hl] [h, l] ->
+ PrimInline $ mconcat
+ [ hr |= toU32 (BNot h)
+ , hl |= toU32 (BNot l)
+ ]
+
+ Word64AddOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_plusWord64" [h0,l0,h1,l1]
+ Word64SubOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_minusWord64" [h0,l0,h1,l1]
+ Word64MulOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_timesWord64" [h0,l0,h1,l1]
+ Word64QuotOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_quotWord64" [h0,l0,h1,l1]
+ Word64RemOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_remWord64" [h0,l0,h1,l1]
+
+------------------------------ Word ---------------------------------------------
+
+ WordAddOp -> \[r] [x,y] -> PrimInline $ r |= (x `Add` y) .>>>. zero_
+ WordAddCOp -> \[r,c] [x,y] -> PrimInline $
+ jVar \t -> mconcat
+ [ t |= x `Add` y
+ , r |= toU32 t
+ , c |= if10 (t .!==. r)
+ ]
+ WordSubCOp -> \[r,c] [x,y] ->
+ PrimInline $ mconcat
+ [ r |= toU32 (Sub x y)
+ , c |= if10 (y .>. x)
+ ]
+ WordAdd2Op -> \[h,l] [x,y] -> PrimInline $ appT [h,l] "h$wordAdd2" [x,y]
+ WordSubOp -> \ [r] [x,y] -> PrimInline $ r |= toU32 (Sub x y)
+ WordMulOp -> \ [r] [x,y] -> PrimInline $ r |= app "h$mulWord32" [x, y]
+ WordMul2Op -> \[h,l] [x,y] -> PrimInline $ appT [h,l] "h$mul2Word32" [x,y]
+ WordQuotOp -> \ [q] [x,y] -> PrimInline $ q |= app "h$quotWord32" [x,y]
+ WordRemOp -> \ [r] [x,y] -> PrimInline $ r |= app "h$remWord32" [x,y]
+ WordQuotRemOp -> \[q,r] [x,y] -> PrimInline $ appT [q,r] "h$quotRemWord32" [x,y]
+ WordQuotRem2Op -> \[q,r] [xh,xl,y] -> PrimInline $ appT [q,r] "h$quotRem2Word32" [xh,xl,y]
+ WordAndOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BAnd x y)
+ WordOrOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BOr x y)
+ WordXorOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BXor x y)
+ WordNotOp -> \[r] [x] -> PrimInline $ r |= toU32 (BNot x)
+ WordSllOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (x .<<. y)
+ WordSrlOp -> \[r] [x,y] -> PrimInline $ r |= x .>>>. y
+ WordToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x
+ WordGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ WordGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ WordEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ WordNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+ WordLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ WordLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ WordToDoubleOp -> \[r] [x] -> PrimInline $ r |= x
+ WordToFloatOp -> \[r] [x] -> PrimInline $ r |= math_fround [x]
+ PopCnt8Op -> \[r] [x] -> PrimInline $ r |= var "h$popCntTab" .! (mask8 x)
+ PopCnt16Op -> \[r] [x] -> PrimInline $ r |= Add (var "h$popCntTab" .! (mask8 x))
+ (var "h$popCntTab" .! (mask8 (x .>>>. Int 8)))
+
+ PopCnt32Op -> \[r] [x] -> PrimInline $ r |= app "h$popCnt32" [x]
+ PopCnt64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$popCnt64" [x1,x2]
+ PopCntOp -> \[r] [x] -> genPrim prof bound ty PopCnt32Op [r] [x]
+ Pdep8Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep8" [s,m]
+ Pdep16Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep16" [s,m]
+ Pdep32Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep32" [s,m]
+ Pdep64Op -> \[ra,rb] [sa,sb,ma,mb] -> PrimInline $ appT [ra,rb] "h$pdep64" [sa,sb,ma,mb]
+ PdepOp -> \rs xs -> genPrim prof bound ty Pdep32Op rs xs
+ Pext8Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext8" [s,m]
+ Pext16Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext16" [s,m]
+ Pext32Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext32" [s,m]
+ Pext64Op -> \[ra,rb] [sa,sb,ma,mb] -> PrimInline $ appT [ra,rb] "h$pext64" [sa,sb,ma,mb]
+ PextOp -> \rs xs -> genPrim prof bound ty Pext32Op rs xs
+
+ ClzOp -> \[r] [x] -> PrimInline $ r |= app "h$clz32" [x]
+ Clz8Op -> \[r] [x] -> PrimInline $ r |= app "h$clz8" [x]
+ Clz16Op -> \[r] [x] -> PrimInline $ r |= app "h$clz16" [x]
+ Clz32Op -> \[r] [x] -> PrimInline $ r |= app "h$clz32" [x]
+ Clz64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$clz64" [x1,x2]
+ CtzOp -> \[r] [x] -> PrimInline $ r |= app "h$ctz32" [x]
+ Ctz8Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz8" [x]
+ Ctz16Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz16" [x]
+ Ctz32Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz32" [x]
+ Ctz64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$ctz64" [x1,x2]
+
+ BSwap16Op -> \[r] [x] -> PrimInline $
+ r |= BOr ((mask8 x) .<<. (Int 8))
+ (mask8 (x .>>>. (Int 8)))
+ BSwap32Op -> \[r] [x] -> PrimInline $
+ r |= toU32 ((x .<<. (Int 24))
+ `BOr` ((BAnd x (Int 0xFF00)) .<<. (Int 8))
+ `BOr` ((BAnd x (Int 0xFF0000)) .>>. (Int 8))
+ `BOr` (x .>>>. (Int 24)))
+ BSwap64Op -> \[r1,r2] [x,y] -> PrimInline $ appT [r1,r2] "h$bswap64" [x,y]
+ BSwapOp -> \[r] [x] -> genPrim prof bound ty BSwap32Op [r] [x]
+
+ BRevOp -> \[r] [w] -> genPrim prof bound ty BRev32Op [r] [w]
+ BRev8Op -> \[r] [w] -> PrimInline $ r |= (app "h$reverseWord" [w] .>>>. 24)
+ BRev16Op -> \[r] [w] -> PrimInline $ r |= (app "h$reverseWord" [w] .>>>. 16)
+ BRev32Op -> \[r] [w] -> PrimInline $ r |= app "h$reverseWord" [w]
+ BRev64Op -> \[rh,rl] [h,l] -> PrimInline $ mconcat [ rl |= app "h$reverseWord" [h]
+ , rh |= app "h$reverseWord" [l]
+ ]
+
+------------------------------ Narrow -------------------------------------------
+
+ Narrow8IntOp -> \[r] [x] -> PrimInline $ r |= signExtend8 x
+ Narrow16IntOp -> \[r] [x] -> PrimInline $ r |= signExtend16 x
+ Narrow32IntOp -> \[r] [x] -> PrimInline $ r |= toI32 x
+ Narrow8WordOp -> \[r] [x] -> PrimInline $ r |= mask8 x
+ Narrow16WordOp -> \[r] [x] -> PrimInline $ r |= mask16 x
+ Narrow32WordOp -> \[r] [x] -> PrimInline $ r |= toU32 x
+
+------------------------------ Double -------------------------------------------
+
+ DoubleGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ DoubleGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ DoubleEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ DoubleNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+ DoubleLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ DoubleLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ DoubleAddOp -> \[r] [x,y] -> PrimInline $ r |= Add x y
+ DoubleSubOp -> \[r] [x,y] -> PrimInline $ r |= Sub x y
+ DoubleMulOp -> \[r] [x,y] -> PrimInline $ r |= Mul x y
+ DoubleDivOp -> \[r] [x,y] -> PrimInline $ r |= Div x y
+ DoubleNegOp -> \[r] [x] -> PrimInline $ r |= Negate x
+ DoubleFabsOp -> \[r] [x] -> PrimInline $ r |= math_abs [x]
+ DoubleToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x
+ DoubleToFloatOp -> \[r] [x] -> PrimInline $ r |= math_fround [x]
+ DoubleExpOp -> \[r] [x] -> PrimInline $ r |= math_exp [x]
+ DoubleExpM1Op -> \[r] [x] -> PrimInline $ r |= math_expm1 [x]
+ DoubleLogOp -> \[r] [x] -> PrimInline $ r |= math_log [x]
+ DoubleLog1POp -> \[r] [x] -> PrimInline $ r |= math_log1p [x]
+ DoubleSqrtOp -> \[r] [x] -> PrimInline $ r |= math_sqrt [x]
+ DoubleSinOp -> \[r] [x] -> PrimInline $ r |= math_sin [x]
+ DoubleCosOp -> \[r] [x] -> PrimInline $ r |= math_cos [x]
+ DoubleTanOp -> \[r] [x] -> PrimInline $ r |= math_tan [x]
+ DoubleAsinOp -> \[r] [x] -> PrimInline $ r |= math_asin [x]
+ DoubleAcosOp -> \[r] [x] -> PrimInline $ r |= math_acos [x]
+ DoubleAtanOp -> \[r] [x] -> PrimInline $ r |= math_atan [x]
+ DoubleSinhOp -> \[r] [x] -> PrimInline $ r |= math_sinh [x]
+ DoubleCoshOp -> \[r] [x] -> PrimInline $ r |= math_cosh [x]
+ DoubleTanhOp -> \[r] [x] -> PrimInline $ r |= math_tanh [x]
+ DoubleAsinhOp -> \[r] [x] -> PrimInline $ r |= math_asinh [x]
+ DoubleAcoshOp -> \[r] [x] -> PrimInline $ r |= math_acosh [x]
+ DoubleAtanhOp -> \[r] [x] -> PrimInline $ r |= math_atanh [x]
+ DoublePowerOp -> \[r] [x,y] -> PrimInline $ r |= math_pow [x,y]
+ DoubleDecode_2IntOp -> \[s,h,l,e] [x] -> PrimInline $ appT [s,h,l,e] "h$decodeDouble2Int" [x]
+ DoubleDecode_Int64Op -> \[s1,s2,e] [d] -> PrimInline $ appT [e,s1,s2] "h$decodeDoubleInt64" [d]
+
+------------------------------ Float --------------------------------------------
+
+ FloatGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)
+ FloatGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)
+ FloatEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)
+ FloatNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)
+ FloatLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)
+ FloatLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)
+ FloatAddOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Add x y]
+ FloatSubOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Sub x y]
+ FloatMulOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Mul x y]
+ FloatDivOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Div x y]
+ FloatNegOp -> \[r] [x] -> PrimInline $ r |= Negate x
+ FloatFabsOp -> \[r] [x] -> PrimInline $ r |= math_abs [x]
+ FloatToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x
+ FloatExpOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_exp [x]]
+ FloatExpM1Op -> \[r] [x] -> PrimInline $ r |= math_fround [math_expm1 [x]]
+ FloatLogOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_log [x]]
+ FloatLog1POp -> \[r] [x] -> PrimInline $ r |= math_fround [math_log1p [x]]
+ FloatSqrtOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sqrt [x]]
+ FloatSinOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sin [x]]
+ FloatCosOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_cos [x]]
+ FloatTanOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_tan [x]]
+ FloatAsinOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_asin [x]]
+ FloatAcosOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_acos [x]]
+ FloatAtanOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_atan [x]]
+ FloatSinhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sinh [x]]
+ FloatCoshOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_cosh [x]]
+ FloatTanhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_tanh [x]]
+ FloatAsinhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_asinh [x]]
+ FloatAcoshOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_acosh [x]]
+ FloatAtanhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_atanh [x]]
+ FloatPowerOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [math_pow [x,y]]
+ FloatToDoubleOp -> \[r] [x] -> PrimInline $ r |= x
+ FloatDecode_IntOp -> \[s,e] [x] -> PrimInline $ appT [s,e] "h$decodeFloatInt" [x]
+
+------------------------------ Arrays -------------------------------------------
+
+ NewArrayOp -> \[r] [l,e] -> PrimInline (newArray r l e)
+ ReadArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)
+ WriteArrayOp -> \[] [a,i,v] -> PrimInline $ boundsChecked bound a i (a .! i |= v)
+ SizeofArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"
+ SizeofMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"
+ IndexArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)
+ UnsafeFreezeArrayOp -> \[r] [a] -> PrimInline $ r |= a
+ UnsafeThawArrayOp -> \[r] [a] -> PrimInline $ r |= a
+ CopyArrayOp -> \[] [a,o1,ma,o2,n] ->
+ PrimInline $ loopBlockS (Int 0) (.<. n) \i ->
+ [ ma .! (Add i o2) |= a .! (Add i o1)
+ , preIncrS i
+ ]
+ CopyMutableArrayOp -> \[] [a1,o1,a2,o2,n] -> PrimInline $ appS "h$copyMutableArray" [a1,o1,a2,o2,n]
+ CloneArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]
+ CloneMutableArrayOp -> \[r] [a,start,n] -> genPrim prof bound ty CloneArrayOp [r] [a,start,n]
+ FreezeArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]
+ ThawArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]
+ CasArrayOp -> \[s,o] [a,i,old,new] -> PrimInline $
+ jVar \x -> mconcat
+ [ x |= a .! i
+ , ifBlockS (x .===. old)
+ [ o |= new
+ , a .! i |= new
+ , s |= zero_
+ ]
+ [ s |= one_
+ , o |= x
+ ]
+ ]
+
+------------------------------ Small Arrays -------------------------------------
+
+ NewSmallArrayOp -> \[a] [n,e] -> PrimInline $ a |= app "h$newArray" [n,e]
+ ReadSmallArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)
+ WriteSmallArrayOp -> \[] [a,i,e] -> PrimInline $ boundsChecked bound a i (a .! i |= e)
+ SizeofSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"
+ SizeofSmallMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"
+ IndexSmallArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)
+ UnsafeFreezeSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a
+ UnsafeThawSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a
+ CopySmallArrayOp -> \[] [s,si,d,di,n] -> PrimInline $
+ loopBlockS (Sub n one_) (.>=. zero_) \i ->
+ [ d .! (Add di i) |= s .! (Add si i)
+ , postDecrS i
+ ]
+ CopySmallMutableArrayOp -> \[] [s,si,d,di,n] -> PrimInline $
+ loopBlockS (Sub n one_) (.>=. zero_) \i ->
+ [ d .! (Add di i) |= s .! (Add si i)
+ , postDecrS i
+ ]
+ CloneSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n
+ CloneSmallMutableArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n
+ FreezeSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n
+ ThawSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n
+ CasSmallArrayOp -> \[s,o] [a,i,old,new] -> PrimInline $ jVar \x -> mconcat
+ [ x |= a .! i
+ , ifBlockS (x .===. old)
+ [ o |= new
+ , a .! i |= new
+ , s |= zero_
+ ]
+ [ s |= one_
+ , o |= x
+ ]
+ ]
+
+------------------------------- Byte Arrays -------------------------------------
+
+ NewByteArrayOp_Char -> \[r] [l] -> PrimInline (newByteArray r l)
+ NewPinnedByteArrayOp_Char -> \[r] [l] -> PrimInline (newByteArray r l)
+ NewAlignedPinnedByteArrayOp_Char -> \[r] [l,_align] -> PrimInline (newByteArray r l)
+ MutableByteArrayIsPinnedOp -> \[r] [_] -> PrimInline $ r |= one_
+ ByteArrayIsPinnedOp -> \[r] [_] -> PrimInline $ r |= one_
+ ByteArrayContents_Char -> \[a,o] [b] -> PrimInline $ mconcat [a |= b, o |= zero_]
+ MutableByteArrayContents_Char -> \[a,o] [b] -> PrimInline $ mconcat [a |= b, o |= zero_]
+ ShrinkMutableByteArrayOp_Char -> \[] [a,n] -> PrimInline $ appS "h$shrinkMutableByteArray" [a,n]
+ ResizeMutableByteArrayOp_Char -> \[r] [a,n] -> PrimInline $ r |= app "h$resizeMutableByteArray" [a,n]
+ UnsafeFreezeByteArrayOp -> \[a] [b] -> PrimInline $ a |= b
+ SizeofByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"
+ SizeofMutableByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"
+ GetSizeofMutableByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"
+ IndexByteArrayOp_Char -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i
+ IndexByteArrayOp_WideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ IndexByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ IndexByteArrayOp_Word -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i
+ IndexByteArrayOp_Addr -> \[r1,r2] [a,i] ->
+ PrimInline . boundsChecked bound a i $ jVar \t -> mconcat
+ [ t |= a .^ "arr"
+ , ifBlockS (t .&&. t .! (i .<<. two_))
+ [ r1 |= t .! (i .<<. two_) .! zero_
+ , r2 |= t .! (i .<<. two_) .! one_
+ ]
+ [ r1 |= null_
+ , r2 |= zero_
+ ]
+ ]
+
+ IndexByteArrayOp_Float -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_f32 a i
+ IndexByteArrayOp_Double -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_f64 a i
+ IndexByteArrayOp_StablePtr -> \[r1,r2] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 3) $ mconcat
+ [ r1 |= var "h$stablePtrBuf"
+ , r2 |= read_i32 a i
+ ]
+ IndexByteArrayOp_Int8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_i8 a i
+ IndexByteArrayOp_Int16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_i16 a i
+ IndexByteArrayOp_Int32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ IndexByteArrayOp_Int64 -> \[h,l] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_i32 a (Add (i .<<. one_) one_)
+ , l |= read_u32 a (i .<<. one_)
+ ]
+ IndexByteArrayOp_Word8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i
+ IndexByteArrayOp_Word16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_u16 a i
+ IndexByteArrayOp_Word32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i
+ IndexByteArrayOp_Word64 -> \[h,l] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_u32 a (Add (i .<<. one_) one_)
+ , l |= read_u32 a (i .<<. one_)
+ ]
+ ReadByteArrayOp_Char -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i
+ ReadByteArrayOp_WideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ ReadByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ ReadByteArrayOp_Word -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i
+ ReadByteArrayOp_Addr -> \[r1,r2] [a,i] ->
+ PrimInline $ jVar \x -> mconcat
+ [ x |= i .<<. two_
+ , ifS (a .^ "arr" .&&. a .^ "arr" .! x)
+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_
+ , r2 |= a .^ "arr" .! x .! one_
+ ])
+ (mconcat [r1 |= null_, r2 |= one_])
+ ]
+ ReadByteArrayOp_Float -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_f32 a i
+ ReadByteArrayOp_Double -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_f64 a i
+ ReadByteArrayOp_StablePtr -> \[r1,r2] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 3) $ mconcat
+ [ r1 |= var "h$stablePtrBuf"
+ , r2 |= read_i32 a i
+ ]
+ ReadByteArrayOp_Int8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_i8 a i
+ ReadByteArrayOp_Int16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_i16 a i
+ ReadByteArrayOp_Int32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ ReadByteArrayOp_Int64 -> \[h,l] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_i32 a (Add (i .<<. one_) one_)
+ , l |= read_u32 a (i .<<. one_)
+ ]
+ ReadByteArrayOp_Word8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i
+ ReadByteArrayOp_Word16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_u16 a i
+ ReadByteArrayOp_Word32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i
+ ReadByteArrayOp_Word64 -> \[h,l] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_u32 a (Add (i .<<. one_) one_)
+ , l |= read_u32 a (i .<<. one_)
+ ]
+ WriteByteArrayOp_Char -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_u8 a i e
+ WriteByteArrayOp_WideChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e
+ WriteByteArrayOp_Int -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e
+ WriteByteArrayOp_Word -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_u32 a i e
+ WriteByteArrayOp_Addr -> \[] [a,i,e1,e2] ->
+ PrimInline $ mconcat
+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty
+ , a .^ "arr" .! (i .<<. two_) |= ValExpr (JList [e1, e2])
+ ]
+ WriteByteArrayOp_Float -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_f32 a i e
+ WriteByteArrayOp_Double -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 7) $ write_f64 a i e
+ WriteByteArrayOp_StablePtr -> \[] [a,i,_e1,e2] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e2
+
+ WriteByteArrayOp_Int8 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_i8 a i e
+ WriteByteArrayOp_Int16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_i16 a i e
+ WriteByteArrayOp_Int32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e
+ WriteByteArrayOp_Int64 -> \[] [a,i,e1,e2] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ write_i32 a (Add (i .<<. one_) one_) e1
+ , write_u32 a (i .<<. one_) e2
+ ]
+ WriteByteArrayOp_Word8 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_u8 a i e
+ WriteByteArrayOp_Word16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_u16 a i e
+ WriteByteArrayOp_Word32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_u32 a i e
+ WriteByteArrayOp_Word64 -> \[] [a,i,h,l] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ write_u32 a (Add (i .<<. one_) one_) h
+ , write_u32 a (i .<<. one_) l
+ ]
+ CompareByteArraysOp -> \[r] [a1,o1,a2,o2,n] ->
+ PrimInline . boundsChecked bound a1 (Add o1 (Sub n 1))
+ . boundsChecked bound a2 (Add o2 (Sub n 1))
+ $ r |= app "h$compareByteArrays" [a1,o1,a2,o2,n]
+
+ CopyByteArrayOp -> \[] [a1,o1,a2,o2,n] ->
+ PrimInline . boundsChecked bound a1 (Add o1 (Sub n 1))
+ . boundsChecked bound a2 (Add o2 (Sub n 1))
+ $ loopBlockS (Sub n one_) (.>=. zero_) \i ->
+ [ write_u8 a2 (Add i o2) (read_u8 a1 (Add i o1))
+ , postDecrS i
+ ]
+ CopyMutableByteArrayOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs
+ CopyByteArrayToAddrOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs
+ CopyMutableByteArrayToAddrOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs
+ CopyAddrToByteArrayOp -> \[] xs@[_ba,_bo,_aa,_ao,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs
+
+ SetByteArrayOp -> \[] [a,o,n,v] ->
+ PrimInline . boundsChecked bound a (Add o (Sub n 1)) $ loopBlockS zero_ (.<. n) \i ->
+ [ write_u8 a (Add o i) v
+ , postIncrS i
+ ]
+
+ AtomicReadByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i
+ AtomicWriteByteArrayOp_Int -> \[] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i v
+ FetchAddByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray Add r a i v
+ FetchSubByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray Sub r a i v
+ FetchAndByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BAnd r a i v
+ FetchOrByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BOr r a i v
+ FetchNandByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray (\x y -> BNot (BAnd x y)) r a i v
+ FetchXorByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BXor r a i v
+
+------------------------------- Addr# ------------------------------------------
+
+ AddrAddOp -> \[a',o'] [a,o,i] -> PrimInline $ mconcat [a' |= a, o' |= Add o i]
+ AddrSubOp -> \[i] [_a1,o1,_a2,o2] -> PrimInline $ i |= Sub o1 o2
+ AddrRemOp -> \[r] [_a,o,i] -> PrimInline $ r |= Mod o i
+ AddrToIntOp -> \[i] [_a,o] -> PrimInline $ i |= o -- only usable for comparisons within one range
+ IntToAddrOp -> \[a,o] [i] -> PrimInline $ mconcat [a |= null_, o |= i]
+ AddrGtOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .>. zero_)
+ AddrGeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .>=. zero_)
+ AddrEqOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .===. zero_)
+ AddrNeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .!==. zero_)
+ AddrLtOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .<. zero_)
+ AddrLeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .<=. zero_)
+
+------------------------------- Addr Indexing: Unboxed Arrays -------------------
+
+ IndexOffAddrOp_Char -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)
+ IndexOffAddrOp_WideChar -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)
+ IndexOffAddrOp_Int -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)
+ IndexOffAddrOp_Word -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)
+ IndexOffAddrOp_Addr -> \[ca,co] [a,o,i] ->
+ PrimInline . boundsChecked bound (a .^ "arr") (off32 o i)
+ $ ifBlockS (a .^ "arr " .&&. a .^ "arr" .! (i .<<. two_))
+ [ ca |= a .^ "arr" .! (off32 o i) .! zero_
+ , co |= a .^ "arr" .! (off32 o i) .! one_
+ ]
+ [ ca |= null_
+ , co |= zero_
+ ]
+ IndexOffAddrOp_Float -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_f32 a (off32 o i)
+ IndexOffAddrOp_Double -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off64 o i) $ c |= read_boff_f64 a (off64 o i)
+ IndexOffAddrOp_StablePtr -> \[c1,c2] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ mconcat
+ [ c1 |= var "h$stablePtrBuf"
+ , c2 |= read_boff_i32 a (off32 o i)
+ ]
+ IndexOffAddrOp_Int8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_i8 a (off8 o i)
+ IndexOffAddrOp_Int16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ c |= read_boff_i16 a (off16 o i)
+ IndexOffAddrOp_Int32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)
+ IndexOffAddrOp_Int64 -> \[h,l] [a,o,i] ->
+ PrimInline $ mconcat
+ [ h |= read_boff_i32 a (Add (off64 o i) (Int 4))
+ , l |= read_boff_u32 a (off64 o i)
+ ]
+ IndexOffAddrOp_Word8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)
+ IndexOffAddrOp_Word16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ c |= read_boff_u16 a (off16 o i)
+ IndexOffAddrOp_Word32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)
+ IndexOffAddrOp_Word64 -> \[h,l] [a,o,i] ->
+ PrimInline $ mconcat
+ [ h |= read_boff_u32 a (Add (off64 o i) (Int 4))
+ , l |= read_boff_u32 a (off64 o i)
+ ]
+ ReadOffAddrOp_Char -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)
+ ReadOffAddrOp_WideChar -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)
+ ReadOffAddrOp_Int -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)
+ ReadOffAddrOp_Word -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)
+ ReadOffAddrOp_Addr -> \[c1,c2] [a,o,i] ->
+ PrimInline $ jVar \x -> mconcat
+ [ x |= i .<<. two_
+ , boundsChecked bound (a .^ "arr") (Add o x) $
+ ifBlockS (a .^ "arr" .&&. a .^ "arr" .! (Add o x))
+ [ c1 |= a .^ "arr" .! (Add o x) .! zero_
+ , c2 |= a .^ "arr" .! (Add o x) .! one_
+ ]
+ [ c1 |= null_
+ , c2 |= zero_
+ ]
+ ]
+ ReadOffAddrOp_Float -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_f32 a (off32 o i)
+ ReadOffAddrOp_Double -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off64 o i) $ c |= read_boff_f64 a (off64 o i)
+ ReadOffAddrOp_StablePtr -> \[c1,c2] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ mconcat
+ [ c1 |= var "h$stablePtrBuf"
+ , c2 |= read_boff_u32 a (off32 o i)
+ ]
+ ReadOffAddrOp_Int8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ AssignStat c $ read_boff_i8 a (off8 o i)
+ ReadOffAddrOp_Int16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ AssignStat c $ read_boff_i16 a (off16 o i)
+ ReadOffAddrOp_Int32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ AssignStat c $ read_boff_i32 a (off32 o i)
+ ReadOffAddrOp_Int64 -> \[h,l] [a,o,i] ->
+ PrimInline $ mconcat
+ [ h |= read_i32 a (Add (off64 o i) (Int 4))
+ , l |= read_u32 a (off64 o i)
+ ]
+ ReadOffAddrOp_Word8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ AssignStat c $ read_boff_u8 a (off8 o i)
+ ReadOffAddrOp_Word16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ AssignStat c $ read_boff_u16 a (off16 o i)
+ ReadOffAddrOp_Word32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ AssignStat c $ read_boff_u32 a (off32 o i)
+ ReadOffAddrOp_Word64 -> \[c1,c2] [a,o,i] ->
+ PrimInline $ mconcat
+ [ c1 |= read_boff_u32 a (Add (off64 o i) (Int 4))
+ , c2 |= read_boff_u32 a (off64 o i)
+ ]
+ WriteOffAddrOp_Char -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_u8 a (off8 o i) v
+ WriteOffAddrOp_WideChar -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v
+ WriteOffAddrOp_Int -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v
+ WriteOffAddrOp_Word -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v
+ WriteOffAddrOp_Addr -> \[] [a,o,i,va,vo] ->
+ PrimInline $ mconcat
+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty
+ , boundsChecked bound (a .^ "arr") (off32 o i) $
+ AssignStat (a .^ "arr" .! (off32 o i)) $ ValExpr (JList [va, vo])
+ ]
+ WriteOffAddrOp_Float -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_f32 a (off32 o i) v
+ WriteOffAddrOp_Double -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off64 o i) $ write_boff_f64 a (off64 o i) v
+ WriteOffAddrOp_StablePtr -> \[] [a,o,i,_v1,v2] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v2
+ WriteOffAddrOp_Int8 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_i8 a (off8 o i) v
+ WriteOffAddrOp_Int16 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off16 o i) $ write_boff_i16 a (off16 o i) v
+ WriteOffAddrOp_Int32 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v
+ WriteOffAddrOp_Int64 -> \[] [a,o,i,v1,v2] -> PrimInline . boundsChecked bound a (off64 o i) $ mconcat
+ [ write_boff_i32 a (Add (off64 o i) (Int 4)) v1
+ , write_boff_u32 a (off64 o i) v2
+ ]
+ WriteOffAddrOp_Word8 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_u8 a (off8 o i) v
+ WriteOffAddrOp_Word16 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off16 o i) $ write_boff_u16 a (off16 o i) v
+ WriteOffAddrOp_Word32 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v
+ WriteOffAddrOp_Word64 -> \[] [a,o,i,v1,v2] -> PrimInline . boundsChecked bound a (off64 o i) $ mconcat
+ [ write_boff_u32 a (Add (off64 o i) (Int 4)) v1
+ , write_boff_u32 a (off64 o i) v2
+ ]
+-- Mutable variables
+ NewMutVarOp -> \[r] [x] -> PrimInline $ r |= New (app "h$MutVar" [x])
+ ReadMutVarOp -> \[r] [m] -> PrimInline $ r |= m .^ "val"
+ WriteMutVarOp -> \[] [m,x] -> PrimInline $ m .^ "val" |= x
+ AtomicModifyMutVar2Op -> \[r1,r2] [m,f] -> PrimInline $ appT [r1,r2] "h$atomicModifyMutVar2" [m,f]
+ AtomicModifyMutVar_Op -> \[r1,r2] [m,f] -> PrimInline $ appT [r1,r2] "h$atomicModifyMutVar" [m,f]
+
+ CasMutVarOp -> \[status,r] [mv,o,n] -> PrimInline $ ifS (mv .^ "val" .===. o)
+ (mconcat [status |= zero_, r |= n, mv .^ "val" |= n])
+ (mconcat [status |= one_ , r |= mv .^ "val"])
+
+------------------------------- Exceptions --------------------------------------
+
+ CatchOp -> \[_r] [a,handler] -> PRPrimCall $ returnS (app "h$catch" [a, handler])
+
+ -- fully ignore the result arity as it can use 1 or 2
+ -- slots, depending on the return type.
+ RaiseOp -> \_r [a] -> PRPrimCall $ returnS (app "h$throw" [a, false_])
+ RaiseIOOp -> \_r [a] -> PRPrimCall $ returnS (app "h$throw" [a, false_])
+ RaiseUnderflowOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypeziunderflowException", false_])
+ RaiseOverflowOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypezioverflowException", false_])
+ RaiseDivZeroOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypezidivZZeroException", false_])
+ MaskAsyncExceptionsOp -> \_r [a] -> PRPrimCall $ returnS (app "h$maskAsync" [a])
+ MaskUninterruptibleOp -> \_r [a] -> PRPrimCall $ returnS (app "h$maskUnintAsync" [a])
+ UnmaskAsyncExceptionsOp -> \_r [a] -> PRPrimCall $ returnS (app "h$unmaskAsync" [a])
+
+ MaskStatus -> \[r] [] -> PrimInline $ r |= app "h$maskStatus" []
+
+------------------------------- STM-accessible Mutable Variables --------------
+
+ AtomicallyOp -> \[_r] [a] -> PRPrimCall $ returnS (app "h$atomically" [a])
+ RetryOp -> \_r [] -> PRPrimCall $ returnS (app "h$stmRetry" [])
+ CatchRetryOp -> \[_r] [a,b] -> PRPrimCall $ returnS (app "h$stmCatchRetry" [a,b])
+ CatchSTMOp -> \[_r] [a,h] -> PRPrimCall $ returnS (app "h$catchStm" [a,h])
+ NewTVarOp -> \[tv] [v] -> PrimInline $ tv |= app "h$newTVar" [v]
+ ReadTVarOp -> \[r] [tv] -> PrimInline $ r |= app "h$readTVar" [tv]
+ ReadTVarIOOp -> \[r] [tv] -> PrimInline $ r |= app "h$readTVarIO" [tv]
+ WriteTVarOp -> \[] [tv,v] -> PrimInline $ appS "h$writeTVar" [tv,v]
+
+------------------------------- Synchronized Mutable Variables ------------------
+
+ NewMVarOp -> \[r] [] -> PrimInline $ r |= New (app "h$MVar" [])
+ TakeMVarOp -> \[_r] [m] -> PRPrimCall $ returnS (app "h$takeMVar" [m])
+ TryTakeMVarOp -> \[r,v] [m] -> PrimInline $ appT [r,v] "h$tryTakeMVar" [m]
+ PutMVarOp -> \[] [m,v] -> PRPrimCall $ returnS (app "h$putMVar" [m,v])
+ TryPutMVarOp -> \[r] [m,v] -> PrimInline $ r |= app "h$tryPutMVar" [m,v]
+ ReadMVarOp -> \[_r] [m] -> PRPrimCall $ returnS (app "h$readMVar" [m])
+ TryReadMVarOp -> \[r,v] [m] -> PrimInline $ mconcat
+ [ v |= m .^ "val"
+ , r |= if01 (v .===. null_)
+ ]
+ IsEmptyMVarOp -> \[r] [m] -> PrimInline $ r |= if10 (m .^ "val" .===. null_)
+
+------------------------------- Delay/Wait Ops ---------------------------------
+
+ DelayOp -> \[] [t] -> PRPrimCall $ returnS (app "h$delayThread" [t])
+ WaitReadOp -> \[] [fd] -> PRPrimCall $ returnS (app "h$waidRead" [fd])
+ WaitWriteOp -> \[] [fd] -> PRPrimCall $ returnS (app "h$waitWrite" [fd])
+
+------------------------------- Concurrency Primitives -------------------------
+
+ ForkOp -> \[_tid] [x] -> PRPrimCall $ returnS (app "h$fork" [x, true_])
+ ForkOnOp -> \[_tid] [_p,x] -> PRPrimCall $ returnS (app "h$fork" [x, true_]) -- ignore processor argument
+ KillThreadOp -> \[] [tid,ex] -> PRPrimCall $ returnS (app "h$killThread" [tid,ex])
+ YieldOp -> \[] [] -> PRPrimCall $ returnS (app "h$yield" [])
+ MyThreadIdOp -> \[r] [] -> PrimInline $ r |= var "h$currentThread"
+ IsCurrentThreadBoundOp -> \[r] [] -> PrimInline $ r |= one_
+ NoDuplicateOp -> \[] [] -> PrimInline mempty -- don't need to do anything as long as we have eager blackholing
+ ThreadStatusOp -> \[stat,cap,locked] [tid] -> PrimInline $ appT [stat, cap, locked] "h$threadStatus" [tid]
+ ListThreadsOp -> \[r] [] -> PrimInline $ r |= var "h$threads"
+ GetThreadLabelOp -> \[r1, r2] [t] -> PrimInline $ appT [r1, r2] "h$getThreadLabel" [t]
+ LabelThreadOp -> \[] [t,l] -> PrimInline $ t .^ "label" |= l
+
+------------------------------- Weak Pointers -----------------------------------
+
+ MkWeakOp -> \[r] [o,b,c] -> PrimInline $ r |= app "h$makeWeak" [o,b,c]
+ MkWeakNoFinalizerOp -> \[r] [o,b] -> PrimInline $ r |= app "h$makeWeakNoFinalizer" [o,b]
+ AddCFinalizerToWeakOp -> \[r] [_a1,_a1o,_a2,_a2o,_i,_a3,_a3o,_w] -> PrimInline $ r |= one_
+ DeRefWeakOp -> \[f,v] [w] -> PrimInline $ mconcat
+ [ v |= w .^ "val"
+ , f |= if01 (v .===. null_)
+ ]
+ FinalizeWeakOp -> \[fl,fin] [w] -> PrimInline $ appT [fin, fl] "h$finalizeWeak" [w]
+ TouchOp -> \[] [_e] -> PrimInline mempty
+ KeepAliveOp -> \[_r] [x, f] -> PRPrimCall $ ReturnStat (app "h$keepAlive" [x, f])
+
+
+------------------------------ Stable pointers and names ------------------------
+
+ MakeStablePtrOp -> \[s1,s2] [a] -> PrimInline $ mconcat
+ [ s1 |= var "h$stablePtrBuf"
+ , s2 |= app "h$makeStablePtr" [a]
+ ]
+ DeRefStablePtrOp -> \[r] [_s1,s2] -> PrimInline $ r |= app "h$deRefStablePtr" [s2]
+ EqStablePtrOp -> \[r] [_sa1,sa2,_sb1,sb2] -> PrimInline $ r |= if10 (sa2 .===. sb2)
+
+ MakeStableNameOp -> \[r] [a] -> PrimInline $ r |= app "h$makeStableName" [a]
+ StableNameToIntOp -> \[r] [s] -> PrimInline $ r |= app "h$stableNameInt" [s]
+
+------------------------------ Compact normal form -----------------------------
+
+ CompactNewOp -> \[c] [s] -> PrimInline $ c |= app "h$compactNew" [s]
+ CompactResizeOp -> \[] [c,s] -> PrimInline $ appS "h$compactResize" [c,s]
+ CompactContainsOp -> \[r] [c,v] -> PrimInline $ r |= app "h$compactContains" [c,v]
+ CompactContainsAnyOp -> \[r] [v] -> PrimInline $ r |= app "h$compactContainsAny" [v]
+ CompactGetFirstBlockOp -> \[ra,ro,s] [c] ->
+ PrimInline $ appT [ra,ro,s] "h$compactGetFirstBlock" [c]
+ CompactGetNextBlockOp -> \[ra,ro,s] [c,a,o] ->
+ PrimInline $ appT [ra,ro,s] "h$compactGetNextBlock" [c,a,o]
+ CompactAllocateBlockOp -> \[ra,ro] [size,sa,so] ->
+ PrimInline $ appT [ra,ro] "h$compactAllocateBlock" [size,sa,so]
+ CompactFixupPointersOp -> \[c,newroota, newrooto] [blocka,blocko,roota,rooto] ->
+ PrimInline $ appT [c,newroota,newrooto] "h$compactFixupPointers" [blocka,blocko,roota,rooto]
+ CompactAdd -> \[_r] [c,o] ->
+ PRPrimCall $ returnS (app "h$compactAdd" [c,o])
+ CompactAddWithSharing -> \[_r] [c,o] ->
+ PRPrimCall $ returnS (app "h$compactAddWithSharing" [c,o])
+ CompactSize -> \[s] [c] ->
+ PrimInline $ s |= app "h$compactSize" [c]
+
+------------------------------ Unsafe pointer equality --------------------------
+
+ ReallyUnsafePtrEqualityOp -> \[r] [p1,p2] -> PrimInline $ r |= if10 (p1 .===. p2)
+
+------------------------------ Parallelism --------------------------------------
+
+ ParOp -> \[r] [_a] -> PrimInline $ r |= zero_
+ SparkOp -> \[r] [a] -> PrimInline $ r |= a
+ SeqOp -> \[_r] [e] -> PRPrimCall $ returnS (app "h$e" [e])
+ NumSparks -> \[r] [] -> PrimInline $ r |= zero_
+
+------------------------------ Tag to enum stuff --------------------------------
+
+ DataToTagOp -> \[_r] [d] -> PRPrimCall $ mconcat
+ [ stack .! PreInc sp |= var "h$dataToTag_e"
+ , returnS (app "h$e" [d])
+ ]
+ TagToEnumOp -> \[r] [tag] -> if
+ | isBoolTy ty -> PrimInline $ r |= IfExpr tag true_ false_
+ | otherwise -> PrimInline $ r |= app "h$tagToEnum" [tag]
+
+------------------------------ Bytecode operations ------------------------------
+
+ AddrToAnyOp -> \[r] [d,_o] -> PrimInline $ r |= d
+
+------------------------------ Profiling (CCS) ------------------------------
+
+ GetCCSOfOp -> \[a, o] [obj] -> if
+ | prof -> PrimInline $ mconcat
+ [ a |= if_ (isObject obj)
+ (app "h$buildCCSPtr" [obj .^ "cc"])
+ null_
+ , o |= zero_
+ ]
+ | otherwise -> PrimInline $ mconcat
+ [ a |= null_
+ , o |= zero_
+ ]
+
+ GetCurrentCCSOp -> \[a, o] [_dummy_arg] ->
+ let ptr = if prof then app "h$buildCCSPtr" [jCurrentCCS]
+ else null_
+ in PrimInline $ mconcat
+ [ a |= ptr
+ , o |= zero_
+ ]
+
+ ClearCCSOp -> \[_r] [x] -> PRPrimCall $ ReturnStat (app "h$clearCCS" [x])
+
+------------------------------ Eventlog -------------------
+
+ TraceEventOp -> \[] [ed,eo] -> PrimInline $ appS "h$traceEvent" [ed,eo]
+ TraceEventBinaryOp -> \[] [ed,eo,len] -> PrimInline $ appS "h$traceEventBinary" [ed,eo,len]
+ TraceMarkerOp -> \[] [ed,eo] -> PrimInline $ appS "h$traceMarker" [ed,eo]
+
+ IndexByteArrayOp_Word8AsChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_boff_u8 a i
+ IndexByteArrayOp_Word8AsWideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ IndexByteArrayOp_Word8AsAddr -> \[r1,r2] [a,i] ->
+ PrimInline $ jVar \x -> mconcat
+ [ x |= i .<<. two_
+ , boundsChecked bound (a .^ "arr") x $
+ ifS (a .^ "arr" .&&. a .^ "arr" .! x)
+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_
+ , r2 |= a .^ "arr" .! x .! one_
+ ])
+ (mconcat [r1 |= null_, r2 |= one_])
+ ]
+ IndexByteArrayOp_Word8AsFloat -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_f32 a i
+ IndexByteArrayOp_Word8AsDouble -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_boff_f64 a i
+ IndexByteArrayOp_Word8AsStablePtr -> \[r1,r2] [a,i] ->
+ PrimInline $ mconcat
+ [ r1 |= var "h$stablePtrBuf"
+ , r2 |= read_boff_i32 a i
+ ]
+ IndexByteArrayOp_Word8AsInt16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_i16 a i
+ IndexByteArrayOp_Word8AsInt32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ IndexByteArrayOp_Word8AsInt64 -> \[h,l] [a,i] ->
+ PrimInline $ mconcat
+ [ h |= read_boff_i32 a (Add i (Int 4))
+ , l |= read_boff_u32 a i
+ ]
+ IndexByteArrayOp_Word8AsInt -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ IndexByteArrayOp_Word8AsWord16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_u16 a i
+ IndexByteArrayOp_Word8AsWord32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i
+ IndexByteArrayOp_Word8AsWord64 -> \[h,l] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_boff_u32 a (Add i (Int 4))
+ , l |= read_boff_u32 a i
+ ]
+ IndexByteArrayOp_Word8AsWord -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i
+
+ ReadByteArrayOp_Word8AsChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_boff_u8 a i
+ ReadByteArrayOp_Word8AsWideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ ReadByteArrayOp_Word8AsAddr -> \[r1,r2] [a,i] ->
+ PrimInline $ jVar \x -> mconcat
+ [ x |= i .<<. two_
+ , boundsChecked bound (a .^ "arr") x $
+ ifS (a .^ "arr" .&&. a .^ "arr" .! x)
+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_
+ , r2 |= a .^ "arr" .! x .! one_
+ ])
+ (mconcat [r1 |= null_, r2 |= one_])
+ ]
+ ReadByteArrayOp_Word8AsFloat -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_f32 a i
+ ReadByteArrayOp_Word8AsDouble -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_boff_f64 a i
+ ReadByteArrayOp_Word8AsStablePtr -> \[r1,r2] [a,i] ->
+ PrimInline $ mconcat
+ [ r1 |= var "h$stablePtrBuf"
+ , r2 |= read_boff_i32 a i
+ ]
+ ReadByteArrayOp_Word8AsInt16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_i16 a i
+ ReadByteArrayOp_Word8AsInt32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ ReadByteArrayOp_Word8AsInt64 -> \[h,l] [a,i] ->
+ PrimInline $ mconcat
+ [ h |= read_boff_i32 a (Add i (Int 4))
+ , l |= read_boff_u32 a i
+ ]
+ ReadByteArrayOp_Word8AsInt -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i
+ ReadByteArrayOp_Word8AsWord16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_u16 a i
+ ReadByteArrayOp_Word8AsWord32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i
+ ReadByteArrayOp_Word8AsWord64 -> \[h,l] [a,i] ->
+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat
+ [ h |= read_boff_u32 a (Add i (Int 4))
+ , l |= read_boff_u32 a i
+ ]
+ ReadByteArrayOp_Word8AsWord -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i
+
+ WriteByteArrayOp_Word8AsChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_boff_i8 a i e
+ WriteByteArrayOp_Word8AsWideChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e
+ WriteByteArrayOp_Word8AsAddr -> \[] [a,i,e1,e2] ->
+ PrimInline $ mconcat
+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty
+ , boundsChecked bound (a .^ "arr") (i .<<. two_) $
+ a .^ "arr" .! (i .<<. two_) |= ValExpr (JList [e1, e2])
+ ]
+
+ WriteByteArrayOp_Word8AsFloat -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_f32 a i e
+ WriteByteArrayOp_Word8AsDouble -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 7) $ write_boff_f64 a i e
+ WriteByteArrayOp_Word8AsStablePtr -> \[] [a,i,_e1,e2] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e2
+ WriteByteArrayOp_Word8AsInt16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_boff_i16 a i e
+ WriteByteArrayOp_Word8AsInt32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e
+ WriteByteArrayOp_Word8AsInt64 -> \[] [a,i,h,l] ->
+ -- JS Numbers are little-endian and 32-bit, so write the lower 4 bytes at i
+ -- then write the higher 4 bytes to i+4
+ PrimInline . boundsChecked bound a i
+ $ mconcat [ write_boff_i32 a (Add i (Int 4)) h
+ , write_boff_u32 a i l
+ ]
+ WriteByteArrayOp_Word8AsInt -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e
+ WriteByteArrayOp_Word8AsWord16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_boff_u16 a i e
+ WriteByteArrayOp_Word8AsWord32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_u32 a i e
+ WriteByteArrayOp_Word8AsWord64 -> \[] [a,i,h,l] ->
+ PrimInline . boundsChecked bound a (Add i 7)
+ $ mconcat [ write_boff_u32 a (Add i (Int 4)) h
+ , write_boff_u32 a i l
+ ]
+ WriteByteArrayOp_Word8AsWord -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_u32 a i e
+
+ CasByteArrayOp_Int -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 3) $ casOp read_i32 write_i32 r a i old new
+ CasByteArrayOp_Int8 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a i $ casOp read_i8 write_i8 r a i old new
+ CasByteArrayOp_Int16 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 1) $ casOp read_i16 write_i16 r a i old new
+ CasByteArrayOp_Int32 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 3) $ casOp read_i32 write_i32 r a i old new
+
+ CasByteArrayOp_Int64 -> \[r_h,r_l] [a,i,old_h,old_l,new_h,new_l] -> PrimInline . boundsChecked bound a (Add (i .<<. one_) one_) $
+ jVar \t_h t_l -> mconcat [ t_h |= read_i32 a (Add (i .<<. one_) one_)
+ , t_l |= read_u32 a (i .<<. one_)
+ , r_h |= t_h
+ , r_l |= t_l
+ , ifS (t_l .===. old_l) -- small optimization, check low bits first, fail fast
+ (ifBlockS (t_h .===. old_h)
+ -- Pre-Condition is good, do the write
+ [ write_i32 a (Add (i .<<. one_) one_) new_h
+ , write_u32 a (i .<<. one_) new_l
+ ]
+ -- no good, don't write
+ mempty)
+ mempty
+ ]
+
+ CasAddrOp_Addr -> \[r_a,r_o] [a1,o1,a2,o2,a3,o3] -> PrimInline $
+ mconcat [ ifS (app "h$comparePointer" [a1,o1,a2,o2])
+ (appS "h$memcpy" [a3,o3,a1,o1,8])
+ mempty
+ , r_a |= a1
+ , r_o |= o1
+ ]
+ CasAddrOp_Word -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u32 write_u32 r a o old new
+ CasAddrOp_Word8 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u8 write_u8 r a o old new
+ CasAddrOp_Word16 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u16 write_u16 r a o old new
+ CasAddrOp_Word32 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u32 write_u32 r a o old new
+ CasAddrOp_Word64 -> \[r_h,r_l] [a,o,old_h,old_l,new_h,new_l] -> PrimInline $
+ mconcat [ r_h |= read_u32 a (Add o (Int 4))
+ , r_l |= read_u32 a o
+ , ifS (r_l .===. old_l)
+ (ifBlockS (r_h .===. old_h)
+ [ write_u32 a (Add o (Int 4)) new_h
+ , write_u32 a o new_l
+ ]
+ mempty)
+ mempty
+ ]
+
+ FetchAddAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr Add r a o v
+ FetchSubAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr Sub r a o v
+ FetchAndAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BAnd r a o v
+ FetchNandAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr ((BNot .) . BAnd) r a o v
+ FetchOrAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BOr r a o v
+ FetchXorAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BXor r a o v
+
+ InterlockedExchange_Addr -> \[r_a,r_o] [a1,o1,_a2,o2] -> PrimInline $
+ -- this primop can't be implemented
+ -- correctly because we don't store
+ -- the array reference part of an Addr#,
+ -- only the offset part.
+ --
+ -- So let's assume that all the array
+ -- references are the same...
+ --
+ -- Note: we could generate an assert
+ -- that checks that a1 === a2. However
+ -- we can't check that the Addr# read
+ -- at Addr# a2[o2] also comes from this
+ -- a1/a2 array.
+ mconcat [ r_a |= a1 -- might be wrong (see above)
+ , r_o |= read_boff_u32 a1 o1
+ -- TODO (see above)
+ -- assert that a1 === a2
+ , write_boff_u32 a1 o1 o2
+ ]
+ InterlockedExchange_Word -> \[r] [a,o,w] -> PrimInline $
+ mconcat [ r |= read_boff_u32 a o
+ , write_boff_u32 a o w
+ ]
+
+ ShrinkSmallMutableArrayOp_Char -> \[] [a,n] -> PrimInline $ appS "h$shrinkMutableCharArray" [a,n]
+ GetSizeofSmallMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"
+
+ AtomicReadAddrOp_Word -> \[r] [a,o] -> PrimInline $ r |= read_boff_u32 a o
+ AtomicWriteAddrOp_Word -> \[] [a,o,w] -> PrimInline $ write_boff_u32 a o w
+
+
+------------------------------ Unhandled primops -------------------
+
+ NewPromptTagOp -> unhandledPrimop op
+ PromptOp -> unhandledPrimop op
+ Control0Op -> unhandledPrimop op
+
+ NewIOPortOp -> unhandledPrimop op
+ ReadIOPortOp -> unhandledPrimop op
+ WriteIOPortOp -> unhandledPrimop op
+
+ GetSparkOp -> unhandledPrimop op
+ AnyToAddrOp -> unhandledPrimop op
+ MkApUpd0_Op -> unhandledPrimop op
+ NewBCOOp -> unhandledPrimop op
+ UnpackClosureOp -> unhandledPrimop op
+ ClosureSizeOp -> unhandledPrimop op
+ GetApStackValOp -> unhandledPrimop op
+ WhereFromOp -> unhandledPrimop op -- should be easily implementable with o.f.n
+
+ SetThreadAllocationCounter -> unhandledPrimop op
+
+------------------------------- Vector -----------------------------------------
+-- For now, vectors are unsupported on the JS backend. Simply put, they do not
+-- make much sense to support given support for arrays and lack of SIMD support
+-- in JS. We could try to roll something special but we would not be able to
+-- give any performance guarentees to the user and so we leave these has
+-- unhandled for now.
+ VecBroadcastOp _ _ _ -> unhandledPrimop op
+ VecPackOp _ _ _ -> unhandledPrimop op
+ VecUnpackOp _ _ _ -> unhandledPrimop op
+ VecInsertOp _ _ _ -> unhandledPrimop op
+ VecAddOp _ _ _ -> unhandledPrimop op
+ VecSubOp _ _ _ -> unhandledPrimop op
+ VecMulOp _ _ _ -> unhandledPrimop op
+ VecDivOp _ _ _ -> unhandledPrimop op
+ VecQuotOp _ _ _ -> unhandledPrimop op
+ VecRemOp _ _ _ -> unhandledPrimop op
+ VecNegOp _ _ _ -> unhandledPrimop op
+ VecIndexByteArrayOp _ _ _ -> unhandledPrimop op
+ VecReadByteArrayOp _ _ _ -> unhandledPrimop op
+ VecWriteByteArrayOp _ _ _ -> unhandledPrimop op
+ VecIndexOffAddrOp _ _ _ -> unhandledPrimop op
+ VecReadOffAddrOp _ _ _ -> unhandledPrimop op
+ VecWriteOffAddrOp _ _ _ -> unhandledPrimop op
+
+ VecIndexScalarByteArrayOp _ _ _ -> unhandledPrimop op
+ VecReadScalarByteArrayOp _ _ _ -> unhandledPrimop op
+ VecWriteScalarByteArrayOp _ _ _ -> unhandledPrimop op
+ VecIndexScalarOffAddrOp _ _ _ -> unhandledPrimop op
+ VecReadScalarOffAddrOp _ _ _ -> unhandledPrimop op
+ VecWriteScalarOffAddrOp _ _ _ -> unhandledPrimop op
+
+ PrefetchByteArrayOp3 -> noOp
+ PrefetchMutableByteArrayOp3 -> noOp
+ PrefetchAddrOp3 -> noOp
+ PrefetchValueOp3 -> noOp
+ PrefetchByteArrayOp2 -> noOp
+ PrefetchMutableByteArrayOp2 -> noOp
+ PrefetchAddrOp2 -> noOp
+ PrefetchValueOp2 -> noOp
+ PrefetchByteArrayOp1 -> noOp
+ PrefetchMutableByteArrayOp1 -> noOp
+ PrefetchAddrOp1 -> noOp
+ PrefetchValueOp1 -> noOp
+ PrefetchByteArrayOp0 -> noOp
+ PrefetchMutableByteArrayOp0 -> noOp
+ PrefetchAddrOp0 -> noOp
+ PrefetchValueOp0 -> noOp
+
+unhandledPrimop :: PrimOp -> [JExpr] -> [JExpr] -> PrimRes
+unhandledPrimop op rs as = PrimInline $ mconcat
+ [ appS "h$log" [toJExpr $ mconcat
+ [ "warning, unhandled primop: "
+ , renderWithContext defaultSDocContext (ppr op)
+ , " "
+ , show (length rs, length as)
+ ]]
+ , appS (mkFastString $ "h$primop_" ++ zEncodeString (renderWithContext defaultSDocContext (ppr op))) as
+ -- copyRes
+ , mconcat $ zipWith (\r reg -> r |= toJExpr reg) rs (enumFrom Ret1)
+ ]
+
+-- | A No Op, used for primops the JS platform cannot or do not support. For
+-- example, the prefetching primops do not make sense on the JS platform because
+-- we do not have enough control over memory to provide any kind of prefetching
+-- mechanism. Hence, these are NoOps.
+noOp :: Foldable f => f a -> f a -> PrimRes
+noOp = const . const $ PrimInline mempty
+
+-- tuple returns
+appT :: [JExpr] -> FastString -> [JExpr] -> JStat
+appT [] f xs = appS f xs
+appT (r:rs) f xs = mconcat
+ [ r |= app f xs
+ , mconcat (zipWith (\r ret -> r |= toJExpr ret) rs (enumFrom Ret1))
+ ]
+
+--------------------------------------------
+-- ByteArray indexing
+--------------------------------------------
+
+-- For every ByteArray, the RTS creates the following views:
+-- i3: Int32 view
+-- u8: Word8 view
+-- u1: Word16 view
+-- f3: Float32 view
+-- f6: Float64 view
+-- dv: generic DataView
+-- It seems a bit weird to mix Int and Word views like this, but perhaps they
+-- are the more common.
+--
+-- See 'h$newByteArray' in 'ghc/rts/js/mem.js' for details.
+--
+-- Note that *byte* indexing can only be done with the generic DataView. Use
+-- read_boff_* and write_boff_* for this.
+--
+-- Other read_* and write_* helpers directly use the more specific views.
+-- Prefer using them over idx_* to make your intent clearer.
+
+idx_i32, idx_u8, idx_u16, idx_f64, idx_f32 :: JExpr -> JExpr -> JExpr
+idx_i32 a i = IdxExpr (a .^ "i3") i
+idx_u8 a i = IdxExpr (a .^ "u8") i
+idx_u16 a i = IdxExpr (a .^ "u1") i
+idx_f64 a i = IdxExpr (a .^ "f6") i
+idx_f32 a i = IdxExpr (a .^ "f3") i
+
+read_u8 :: JExpr -> JExpr -> JExpr
+read_u8 a i = idx_u8 a i
+
+read_u16 :: JExpr -> JExpr -> JExpr
+read_u16 a i = idx_u16 a i
+
+read_u32 :: JExpr -> JExpr -> JExpr
+read_u32 a i = toU32 (idx_i32 a i)
+
+read_i8 :: JExpr -> JExpr -> JExpr
+read_i8 a i = signExtend8 (idx_u8 a i)
+
+read_i16 :: JExpr -> JExpr -> JExpr
+read_i16 a i = signExtend16 (idx_u16 a i)
+
+read_i32 :: JExpr -> JExpr -> JExpr
+read_i32 a i = idx_i32 a i
+
+read_f32 :: JExpr -> JExpr -> JExpr
+read_f32 a i = idx_f32 a i
+
+read_f64 :: JExpr -> JExpr -> JExpr
+read_f64 a i = idx_f64 a i
+
+write_u8 :: JExpr -> JExpr -> JExpr -> JStat
+write_u8 a i v = idx_u8 a i |= v
+
+write_u16 :: JExpr -> JExpr -> JExpr -> JStat
+write_u16 a i v = idx_u16 a i |= v
+
+write_u32 :: JExpr -> JExpr -> JExpr -> JStat
+write_u32 a i v = idx_i32 a i |= v
+
+write_i8 :: JExpr -> JExpr -> JExpr -> JStat
+write_i8 a i v = idx_u8 a i |= v
+
+write_i16 :: JExpr -> JExpr -> JExpr -> JStat
+write_i16 a i v = idx_u16 a i |= v
+
+write_i32 :: JExpr -> JExpr -> JExpr -> JStat
+write_i32 a i v = idx_i32 a i |= v
+
+write_f32 :: JExpr -> JExpr -> JExpr -> JStat
+write_f32 a i v = idx_f32 a i |= v
+
+write_f64 :: JExpr -> JExpr -> JExpr -> JStat
+write_f64 a i v = idx_f64 a i |= v
+
+-- Data View helper functions: byte indexed!
+--
+-- The argument list consists of the array @a@, the index @i@, and the new value
+-- to set (in the case of a setter) @v@.
+
+write_boff_i8, write_boff_u8, write_boff_i16, write_boff_u16, write_boff_i32, write_boff_u32, write_boff_f32, write_boff_f64 :: JExpr -> JExpr -> JExpr -> JStat
+write_boff_i8 a i v = write_i8 a i v
+write_boff_u8 a i v = write_u8 a i v
+write_boff_i16 a i v = ApplStat (a .^ "dv" .^ "setInt16" ) [i, v, true_]
+write_boff_u16 a i v = ApplStat (a .^ "dv" .^ "setUint16" ) [i, v, true_]
+write_boff_i32 a i v = ApplStat (a .^ "dv" .^ "setInt32" ) [i, v, true_]
+write_boff_u32 a i v = ApplStat (a .^ "dv" .^ "setUint32" ) [i, v, true_]
+write_boff_f32 a i v = ApplStat (a .^ "dv" .^ "setFloat32") [i, v, true_]
+write_boff_f64 a i v = ApplStat (a .^ "dv" .^ "setFloat64") [i, v, true_]
+
+read_boff_i8, read_boff_u8, read_boff_i16, read_boff_u16, read_boff_i32, read_boff_u32, read_boff_f32, read_boff_f64 :: JExpr -> JExpr -> JExpr
+read_boff_i8 a i = read_i8 a i
+read_boff_u8 a i = read_u8 a i
+read_boff_i16 a i = ApplExpr (a .^ "dv" .^ "getInt16" ) [i, true_]
+read_boff_u16 a i = ApplExpr (a .^ "dv" .^ "getUint16" ) [i, true_]
+read_boff_i32 a i = ApplExpr (a .^ "dv" .^ "getInt32" ) [i, true_]
+read_boff_u32 a i = ApplExpr (a .^ "dv" .^ "getUint32" ) [i, true_]
+read_boff_f32 a i = ApplExpr (a .^ "dv" .^ "getFloat32") [i, true_]
+read_boff_f64 a i = ApplExpr (a .^ "dv" .^ "getFloat64") [i, true_]
+
+fetchOpByteArray :: (JExpr -> JExpr -> JExpr) -> JExpr -> JExpr -> JExpr -> JExpr -> JStat
+fetchOpByteArray op tgt src i v = mconcat
+ [ tgt |= read_i32 src i
+ , write_i32 src i (op tgt v)
+ ]
+
+fetchOpAddr :: (JExpr -> JExpr -> JExpr) -> JExpr -> JExpr -> JExpr -> JExpr -> JStat
+fetchOpAddr op tgt src i v = mconcat
+ [ tgt |= read_boff_u32 src i
+ , write_boff_u32 src i (op tgt v)
+ ]
+
+casOp
+ :: (JExpr -> JExpr -> JExpr) -- read
+ -> (JExpr -> JExpr -> JExpr -> JStat) -- write
+ -> JExpr -- target register to store result
+ -> JExpr -- source arrays
+ -> JExpr -- index
+ -> JExpr -- old value to compare
+ -> JExpr -- new value to write
+ -> JStat
+casOp read write tgt src i old new = mconcat
+ [ tgt |= read src i
+ , ifS (tgt .===. old)
+ (write src i new)
+ mempty
+ ]
+
+--------------------------------------------------------------------------------
+-- Lifted Arrays
+--------------------------------------------------------------------------------
+-- | lifted arrays
+cloneArray :: JExpr -> JExpr -> Maybe JExpr -> JExpr -> JStat
+cloneArray tgt src mb_offset len = mconcat
+ [ tgt |= ApplExpr (src .^ "slice") [start, end]
+ , tgt .^ closureMeta_ |= zero_
+ , tgt .^ "__ghcjsArray" |= true_
+ ]
+ where
+ start = fromMaybe zero_ mb_offset
+ end = maybe len (Add len) mb_offset
+
+newArray :: JExpr -> JExpr -> JExpr -> JStat
+newArray tgt len elem =
+ tgt |= app "h$newArray" [len, elem]
+
+newByteArray :: JExpr -> JExpr -> JStat
+newByteArray tgt len =
+ tgt |= app "h$newByteArray" [len]
+
+boundsChecked :: Bool -- ^ Should we do bounds checking?
+ -> JExpr -- ^ Array
+ -> JExpr -- ^ Index
+ -> JStat -- ^ Result
+ -> JStat
+boundsChecked False _ _ r = r
+boundsChecked True xs i r =
+ ifS ((i .<. xs .^ "length") .&&. (i .>=. zero_))
+ r
+ (returnS $ app "h$exitProcess" [Int 134])
+
+-- e|0 (32 bit signed integer truncation) required because of JS numbers. e|0
+-- converts e to an Int32. Note that e|0 _is still a Double_ because JavaScript.
+-- So (x|0) * (y|0) can still return values outside of the Int32 range. You have
+-- been warned!
+toI32 :: JExpr -> JExpr
+toI32 e = BOr e zero_
+
+-- e>>>0 (32 bit unsigned integer truncation)
+-- required because of JS numbers. e>>>0 converts e to a Word32
+-- so (-2147483648) >>> 0 = 2147483648
+-- and ((-2147483648) >>>0) | 0 = -2147483648
+toU32 :: JExpr -> JExpr
+toU32 e = e .>>>. zero_
+
+quotShortInt :: Int -> JExpr -> JExpr -> JExpr
+quotShortInt bits x y = BAnd (signed x `Div` signed y) mask
+ where
+ signed z = (z .<<. shift) .>>. shift
+ shift = toJExpr (32 - bits)
+ mask = toJExpr (((2::Integer) ^ bits) - 1)
+
+remShortInt :: Int -> JExpr -> JExpr -> JExpr
+remShortInt bits x y = BAnd (signed x `Mod` signed y) mask
+ where
+ signed z = (z .<<. shift) .>>. shift
+ shift = toJExpr (32 - bits)
+ mask = toJExpr (((2::Integer) ^ bits) - 1)
diff --git a/compiler/GHC/StgToJS/Printer.hs b/compiler/GHC/StgToJS/Printer.hs
new file mode 100644
index 0000000000..086f30ba07
--- /dev/null
+++ b/compiler/GHC/StgToJS/Printer.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Printer
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Stability : experimental
+--
+-- Custom prettyprinter for JS AST uses the JS PPr module for most of
+-- the work
+--
+--
+-----------------------------------------------------------------------------
+module GHC.StgToJS.Printer
+ ( pretty
+ , ghcjsRenderJs
+ , prettyBlock
+ )
+where
+
+import GHC.Prelude
+import GHC.Int
+import GHC.Exts
+
+import GHC.JS.Syntax
+import GHC.JS.Ppr
+
+import GHC.Utils.Ppr as PP
+import GHC.Data.FastString
+import GHC.Types.Unique.Map
+
+import Data.List (sortOn)
+import Data.Char (isAlpha,isDigit,ord)
+import qualified Data.ByteString.Short as SBS
+
+pretty :: JStat -> Doc
+pretty = jsToDocR ghcjsRenderJs
+
+ghcjsRenderJs :: RenderJs
+ghcjsRenderJs = defaultRenderJs
+ { renderJsV = ghcjsRenderJsV
+ , renderJsS = ghcjsRenderJsS
+ , renderJsI = ghcjsRenderJsI
+ }
+
+hdd :: SBS.ShortByteString
+hdd = SBS.pack (map (fromIntegral . ord) "h$$")
+
+ghcjsRenderJsI :: RenderJs -> Ident -> Doc
+ghcjsRenderJsI _ (TxtI fs)
+ -- Fresh symbols are prefixed with "h$$". They aren't explicitly referred by
+ -- name in user code, only in compiled code. Hence we can rename them if we do
+ -- it consistently in all the linked code.
+ --
+ -- These symbols are usually very large because their name includes the
+ -- unit-id, the module name, and some unique number. So we rename these
+ -- symbols with a much shorter globally unique number.
+ --
+ -- Here we reuse their FastString unique for this purpose! Note that it only
+ -- works if we pretty-print all the JS code linked together at once, which we
+ -- currently do. GHCJS used to maintain a CompactorState to support
+ -- incremental linking: it contained the mapping between original symbols and
+ -- their renaming.
+ | hdd `SBS.isPrefixOf` fastStringToShortByteString fs
+ , u <- uniqueOfFS fs
+ = text "h$$" <> hexDoc (fromIntegral u)
+ | otherwise
+ = ftext fs
+
+-- | Render as an hexadecimal number in reversed order (because it's faster and we
+-- don't care about the actual value).
+hexDoc :: Word -> Doc
+hexDoc 0 = char '0'
+hexDoc v = text $ go v
+ where
+ sym (I# i) = C# (indexCharOffAddr# chars i)
+ chars = "0123456789abcdef"#
+ go = \case
+ 0 -> []
+ n -> sym (fromIntegral (n .&. 0x0F))
+ : sym (fromIntegral ((n .&. 0xF0) `shiftR` 4))
+ : go (n `shiftR` 8)
+
+
+
+
+-- attempt to resugar some of the common constructs
+ghcjsRenderJsS :: RenderJs -> JStat -> Doc
+ghcjsRenderJsS r (BlockStat xs) = prettyBlock r (flattenBlocks xs)
+ghcjsRenderJsS r s = renderJsS defaultRenderJs r s
+
+-- don't quote keys in our object literals, so closure compiler works
+ghcjsRenderJsV :: RenderJs -> JVal -> Doc
+ghcjsRenderJsV r (JHash m)
+ | isNullUniqMap m = text "{}"
+ | otherwise = braceNest . PP.fsep . punctuate comma .
+ map (\(x,y) -> quoteIfRequired x <> PP.colon <+> jsToDocR r y)
+ -- nonDetEltsUniqMap doesn't introduce non-determinism here because
+ -- we sort the elements lexically
+ . sortOn (LexicalFastString . fst) $ nonDetEltsUniqMap m
+ where
+ quoteIfRequired :: FastString -> Doc
+ quoteIfRequired x
+ | isUnquotedKey x' = text x'
+ | otherwise = PP.squotes (text x')
+ where x' = unpackFS x
+
+ isUnquotedKey :: String -> Bool
+ isUnquotedKey x | null x = False
+ | all isDigit x = True
+ | otherwise = validFirstIdent (head x)
+ && all validOtherIdent (tail x)
+
+
+ validFirstIdent c = c == '_' || c == '$' || isAlpha c
+ validOtherIdent c = isAlpha c || isDigit c
+ghcjsRenderJsV r v = renderJsV defaultRenderJs r v
+
+prettyBlock :: RenderJs -> [JStat] -> Doc
+prettyBlock r xs = vcat $ map addSemi (prettyBlock' r xs)
+
+-- recognize common patterns in a block and convert them to more idiomatic/concise javascript
+prettyBlock' :: RenderJs -> [JStat] -> [Doc]
+-- return/...
+prettyBlock' r ( x@(ReturnStat _)
+ : xs
+ )
+ | not (null xs)
+ = prettyBlock' r [x]
+-- declare/assign
+prettyBlock' r ( (DeclStat i Nothing)
+ : (AssignStat (ValExpr (JVar i')) v)
+ : xs
+ )
+ | i == i'
+ = prettyBlock' r (DeclStat i (Just v) : xs)
+
+-- resugar for loops with/without var declaration
+prettyBlock' r ( (DeclStat i (Just v0))
+ : (WhileStat False p (BlockStat bs))
+ : xs
+ )
+ | not (null flat) && isForUpdStat (last flat)
+ = mkFor r True i v0 p (last flat) (init flat) : prettyBlock' r xs
+ where
+ flat = flattenBlocks bs
+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) v0)
+ : (WhileStat False p (BlockStat bs))
+ : xs
+ )
+ | not (null flat) && isForUpdStat (last flat)
+ = mkFor r False i v0 p (last flat) (init flat) : prettyBlock' r xs
+ where
+ flat = flattenBlocks bs
+
+-- global function (does not preserve semantics but works for GHCJS)
+prettyBlock' r ( (DeclStat i (Just (ValExpr (JFunc is b))))
+ : xs
+ )
+ = (hangBrace (text "function" <+> jsToDocR r i <> parens (fsep . punctuate comma . map (jsToDocR r) $ is))
+ (jsToDocR r b)
+ ) : prettyBlock' r xs
+-- modify/assign operators
+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr AddOp (ValExpr (JVar i')) (ValExpr (JInt 1))))
+ : xs
+ )
+ | i == i' = (text "++" <> jsToDocR r i) : prettyBlock' r xs
+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr SubOp (ValExpr (JVar i')) (ValExpr (JInt 1))))
+ : xs
+ )
+ | i == i' = (text "--" <> jsToDocR r i) : prettyBlock' r xs
+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr AddOp (ValExpr (JVar i')) e))
+ : xs
+ )
+ | i == i' = (jsToDocR r i <+> text "+=" <+> jsToDocR r e) : prettyBlock' r xs
+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr SubOp (ValExpr (JVar i')) e))
+ : xs
+ )
+ | i == i' = (jsToDocR r i <+> text "-=" <+> jsToDocR r e) : prettyBlock' r xs
+
+
+prettyBlock' r (x:xs) = jsToDocR r x : prettyBlock' r xs
+prettyBlock' _ [] = []
+
+-- build the for block
+mkFor :: RenderJs -> Bool -> Ident -> JExpr -> JExpr -> JStat -> [JStat] -> Doc
+mkFor r decl i v0 p s1 sb = hangBrace (text "for" <> forCond)
+ (jsToDocR r $ BlockStat sb)
+ where
+ c0 | decl = text "var" <+> jsToDocR r i <+> char '=' <+> jsToDocR r v0
+ | otherwise = jsToDocR r i <+> char '=' <+> jsToDocR r v0
+ forCond = parens $ hcat $ interSemi
+ [ c0
+ , jsToDocR r p
+ , parens (jsToDocR r s1)
+ ]
+
+-- check if a statement is suitable to be converted to something in the for(;;x) position
+isForUpdStat :: JStat -> Bool
+isForUpdStat UOpStat {} = True
+isForUpdStat AssignStat {} = True
+isForUpdStat ApplStat {} = True
+isForUpdStat _ = False
+
+interSemi :: [Doc] -> [Doc]
+interSemi [] = [PP.empty]
+interSemi [s] = [s]
+interSemi (x:xs) = x <> text ";" : interSemi xs
+
+addSemi :: Doc -> Doc
+addSemi x = x <> text ";"
diff --git a/compiler/GHC/StgToJS/Profiling.hs b/compiler/GHC/StgToJS/Profiling.hs
new file mode 100644
index 0000000000..cd27604082
--- /dev/null
+++ b/compiler/GHC/StgToJS/Profiling.hs
@@ -0,0 +1,178 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.Profiling
+ ( initCostCentres
+ , emitCostCentreDecl
+ , emitCostCentreStackDecl
+ , enterCostCentreFun
+ , enterCostCentreThunk
+ , setCC
+ , pushRestoreCCS
+ , jCurrentCCS
+ , jCafCCS
+ , jSystemCCS
+ , costCentreLbl
+ , costCentreStackLbl
+ , singletonCCSLbl
+ , ccsVarJ
+ -- * Predicates
+ , profiling
+ , ifProfiling
+ , ifProfilingM
+ -- * helpers
+ , profStat
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Regs
+import GHC.StgToJS.Types
+import GHC.StgToJS.Symbols
+import GHC.StgToJS.Monad
+
+import GHC.Types.CostCentre
+
+import GHC.Data.FastString
+import GHC.Unit.Module
+import GHC.Utils.Encoding
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+import qualified Control.Monad.Trans.State.Strict as State
+
+--------------------------------------------------------------------------------
+-- Initialization
+
+initCostCentres :: CollectedCCs -> G ()
+initCostCentres (local_CCs, singleton_CCSs) = do
+ mapM_ emitCostCentreDecl local_CCs
+ mapM_ emitCostCentreStackDecl singleton_CCSs
+
+emitCostCentreDecl :: CostCentre -> G ()
+emitCostCentreDecl cc = do
+ ccsLbl <- costCentreLbl cc
+ let is_caf = isCafCC cc
+ label = costCentreUserName cc
+ modl = moduleNameString $ moduleName $ cc_mod cc
+ loc = renderWithContext defaultSDocContext (ppr (costCentreSrcSpan cc))
+ js = ccsLbl ||= UOpExpr NewOp (ApplExpr (var "h$CC")
+ [ toJExpr label
+ , toJExpr modl
+ , toJExpr loc
+ , toJExpr is_caf
+ ])
+ emitGlobal js
+
+emitCostCentreStackDecl :: CostCentreStack -> G ()
+emitCostCentreStackDecl ccs =
+ case maybeSingletonCCS ccs of
+ Just cc -> do
+ ccsLbl <- singletonCCSLbl cc
+ ccLbl <- costCentreLbl cc
+ let js = ccsLbl ||= UOpExpr NewOp (ApplExpr (var "h$CCS") [null_, toJExpr ccLbl])
+ emitGlobal js
+ Nothing -> pprPanic "emitCostCentreStackDecl" (ppr ccs)
+
+--------------------------------------------------------------------------------
+-- Entering to cost-centres
+
+enterCostCentreFun :: CostCentreStack -> JStat
+enterCostCentreFun ccs
+ | isCurrentCCS ccs = ApplStat (var "h$enterFunCCS") [jCurrentCCS, r1 .^ "cc"]
+ | otherwise = mempty -- top-level function, nothing to do
+
+enterCostCentreThunk :: JStat
+enterCostCentreThunk = ApplStat (var "h$enterThunkCCS") [r1 .^ "cc"]
+
+setCC :: CostCentre -> Bool -> Bool -> G JStat
+setCC cc _tick True = do
+ ccI@(TxtI _ccLbl) <- costCentreLbl cc
+ addDependency $ OtherSymb (cc_mod cc)
+ (moduleGlobalSymbol $ cc_mod cc)
+ return $ jCurrentCCS |= ApplExpr (var "h$pushCostCentre") [jCurrentCCS, toJExpr ccI]
+setCC _cc _tick _push = return mempty
+
+pushRestoreCCS :: JStat
+pushRestoreCCS = ApplStat (var "h$pushRestoreCCS") []
+
+--------------------------------------------------------------------------------
+-- Some cost-centre stacks to be used in generator
+
+jCurrentCCS :: JExpr
+jCurrentCCS = var "h$currentThread" .^ "ccs"
+
+jCafCCS :: JExpr
+jCafCCS = var "h$CAF"
+
+jSystemCCS :: JExpr
+jSystemCCS = var "h$CCS_SYSTEM"
+--------------------------------------------------------------------------------
+-- Helpers for generating profiling related things
+
+profiling :: G Bool
+profiling = csProf <$> getSettings
+
+ifProfiling :: Monoid m => m -> G m
+ifProfiling m = do
+ prof <- profiling
+ return $ if prof then m else mempty
+
+ifProfilingM :: Monoid m => G m -> G m
+ifProfilingM m = do
+ prof <- profiling
+ if prof then m else return mempty
+
+-- | If profiling is enabled, then use input JStat, else ignore
+profStat :: StgToJSConfig -> JStat -> JStat
+profStat cfg e = if csProf cfg then e else mempty
+--------------------------------------------------------------------------------
+-- Generating cost-centre and cost-centre stack variables
+
+costCentreLbl' :: CostCentre -> G String
+costCentreLbl' cc = do
+ curModl <- State.gets gsModule
+ let lbl = renderWithContext defaultSDocContext
+ $ withPprStyle PprCode (ppr cc)
+ return . ("h$"++) . zEncodeString $
+ moduleNameColons (moduleName curModl) ++ "_" ++ if isCafCC cc then "CAF_ccs" else lbl
+
+costCentreLbl :: CostCentre -> G Ident
+costCentreLbl cc = TxtI . mkFastString <$> costCentreLbl' cc
+
+costCentreStackLbl' :: CostCentreStack -> G (Maybe String)
+costCentreStackLbl' ccs = do
+ ifProfilingM f
+ where
+ f | isCurrentCCS ccs = return $ Just "h$currentThread.ccs"
+ | dontCareCCS == ccs = return $ Just "h$CCS_DONT_CARE"
+ | otherwise =
+ case maybeSingletonCCS ccs of
+ Just cc -> Just <$> singletonCCSLbl' cc
+ Nothing -> pure Nothing
+
+costCentreStackLbl :: CostCentreStack -> G (Maybe Ident)
+costCentreStackLbl ccs = fmap (TxtI . mkFastString) <$> costCentreStackLbl' ccs
+
+singletonCCSLbl' :: CostCentre -> G String
+singletonCCSLbl' cc = do
+ curModl <- State.gets gsModule
+ ccLbl <- costCentreLbl' cc
+ let ccsLbl = ccLbl ++ "_ccs"
+ return . zEncodeString $ mconcat
+ [ moduleNameColons (moduleName curModl)
+ , "_"
+ , ccsLbl
+ ]
+
+singletonCCSLbl :: CostCentre -> G Ident
+singletonCCSLbl cc = TxtI . mkFastString <$> singletonCCSLbl' cc
+
+ccsVarJ :: CostCentreStack -> G (Maybe JExpr)
+ccsVarJ ccs = do
+ prof <- profiling
+ if prof
+ then fmap (ValExpr . JVar) <$> costCentreStackLbl ccs
+ else pure Nothing
diff --git a/compiler/GHC/StgToJS/Regs.hs b/compiler/GHC/StgToJS/Regs.hs
new file mode 100644
index 0000000000..ea482d4036
--- /dev/null
+++ b/compiler/GHC/StgToJS/Regs.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.Regs
+ ( StgReg (..)
+ , Special(..)
+ , sp
+ , stack
+ , r1, r2, r3, r4
+ , regsFromR1
+ , regsFromR2
+ , jsRegsFromR1
+ , jsRegsFromR2
+ , StgRet (..)
+ , jsRegToInt
+ , intToJSReg
+ , jsReg
+ , maxReg
+ , minReg
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.Data.FastString
+
+import Data.Array
+import Data.Char
+
+-- | General purpose "registers"
+--
+-- The JS backend arbitrarily supports 128 registers
+data StgReg
+ = R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8
+ | R9 | R10 | R11 | R12 | R13 | R14 | R15 | R16
+ | R17 | R18 | R19 | R20 | R21 | R22 | R23 | R24
+ | R25 | R26 | R27 | R28 | R29 | R30 | R31 | R32
+ | R33 | R34 | R35 | R36 | R37 | R38 | R39 | R40
+ | R41 | R42 | R43 | R44 | R45 | R46 | R47 | R48
+ | R49 | R50 | R51 | R52 | R53 | R54 | R55 | R56
+ | R57 | R58 | R59 | R60 | R61 | R62 | R63 | R64
+ | R65 | R66 | R67 | R68 | R69 | R70 | R71 | R72
+ | R73 | R74 | R75 | R76 | R77 | R78 | R79 | R80
+ | R81 | R82 | R83 | R84 | R85 | R86 | R87 | R88
+ | R89 | R90 | R91 | R92 | R93 | R94 | R95 | R96
+ | R97 | R98 | R99 | R100 | R101 | R102 | R103 | R104
+ | R105 | R106 | R107 | R108 | R109 | R110 | R111 | R112
+ | R113 | R114 | R115 | R116 | R117 | R118 | R119 | R120
+ | R121 | R122 | R123 | R124 | R125 | R126 | R127 | R128
+ deriving (Eq, Ord, Show, Enum, Bounded, Ix)
+
+-- | Stack registers
+data Special
+ = Stack
+ | Sp
+ deriving (Show, Eq)
+
+-- | Return registers
+--
+-- Extra results from foreign calls can be stored here (while first result is
+-- directly returned)
+data StgRet = Ret1 | Ret2 | Ret3 | Ret4 | Ret5 | Ret6 | Ret7 | Ret8 | Ret9 | Ret10
+ deriving (Eq, Ord, Show, Enum, Bounded, Ix)
+
+instance ToJExpr Special where
+ toJExpr Stack = var "h$stack"
+ toJExpr Sp = var "h$sp"
+
+instance ToJExpr StgReg where
+ toJExpr r = registers ! r
+
+instance ToJExpr StgRet where
+ toJExpr r = rets ! r
+
+---------------------------------------------------
+-- helpers
+---------------------------------------------------
+
+sp :: JExpr
+sp = toJExpr Sp
+
+stack :: JExpr
+stack = toJExpr Stack
+
+r1, r2, r3, r4 :: JExpr
+r1 = toJExpr R1
+r2 = toJExpr R2
+r3 = toJExpr R3
+r4 = toJExpr R4
+
+
+jsRegToInt :: StgReg -> Int
+jsRegToInt = (+1) . fromEnum
+
+intToJSReg :: Int -> StgReg
+intToJSReg r = toEnum (r - 1)
+
+jsReg :: Int -> JExpr
+jsReg r = toJExpr (intToJSReg r)
+
+maxReg :: Int
+maxReg = jsRegToInt maxBound
+
+minReg :: Int
+minReg = jsRegToInt minBound
+
+-- | List of registers, starting from R1
+regsFromR1 :: [StgReg]
+regsFromR1 = enumFrom R1
+
+-- | List of registers, starting from R2
+regsFromR2 :: [StgReg]
+regsFromR2 = tail regsFromR1
+
+-- | List of registers, starting from R1 as JExpr
+jsRegsFromR1 :: [JExpr]
+jsRegsFromR1 = fmap toJExpr regsFromR1
+
+-- | List of registers, starting from R2 as JExpr
+jsRegsFromR2 :: [JExpr]
+jsRegsFromR2 = tail jsRegsFromR1
+
+---------------------------------------------------
+-- caches
+---------------------------------------------------
+
+-- cache JExpr representing StgReg
+registers :: Array StgReg JExpr
+registers = listArray (minBound, maxBound) (map regN regsFromR1)
+ where
+ regN r
+ | fromEnum r < 32 = var . mkFastString . ("h$"++) . map toLower . show $ r
+ | otherwise = IdxExpr (var "h$regs")
+ (toJExpr ((fromEnum r) - 32))
+
+-- cache JExpr representing StgRet
+rets :: Array StgRet JExpr
+rets = listArray (minBound, maxBound) (map retN (enumFrom Ret1))
+ where
+ retN = var . mkFastString . ("h$"++) . map toLower . show
diff --git a/compiler/GHC/StgToJS/Rts/Rts.hs b/compiler/GHC/StgToJS/Rts/Rts.hs
new file mode 100644
index 0000000000..55e1a3f312
--- /dev/null
+++ b/compiler/GHC/StgToJS/Rts/Rts.hs
@@ -0,0 +1,661 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+{-# OPTIONS_GHC -O0 #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Rts.Rts
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Top level driver of the JavaScript Backend RTS. This file is an
+-- implementation of the JS RTS for the JS backend written as an EDSL in
+-- Haskell. It assumes the existence of pre-generated JS functions, included as
+-- js-sources in base. These functions are similarly assumed for non-inline
+-- Primops, See 'GHC.StgToJS.Prim'. Most of the elements in this module are
+-- constants in Haskell Land which define pieces of the JS RTS.
+--
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Rts.Rts where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+import GHC.JS.Transform
+
+import GHC.StgToJS.Apply
+import GHC.StgToJS.Closure
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Printer
+import GHC.StgToJS.Profiling
+import GHC.StgToJS.Regs
+import GHC.StgToJS.Types
+import GHC.StgToJS.Stack
+
+import GHC.Data.FastString
+import GHC.Types.Unique.Map
+
+import Data.Array
+import Data.Monoid
+import Data.Char (toLower, toUpper)
+import qualified Data.Bits as Bits
+
+-- | The garbageCollector resets registers and result variables.
+garbageCollector :: JStat
+garbageCollector =
+ mconcat [ TxtI "h$resetRegisters" ||= jLam (mconcat $ map resetRegister [minBound..maxBound])
+ , TxtI "h$resetResultVars" ||= jLam (mconcat $ map resetResultVar [minBound..maxBound])
+ ]
+
+-- | Reset the register 'r' in JS Land. Note that this "resets" by setting the
+-- register to a dummy variable called "null", /not/ by setting to JS's nil
+-- value.
+resetRegister :: StgReg -> JStat
+resetRegister r = toJExpr r |= null_
+
+-- | Reset the return variable 'r' in JS Land. Note that this "resets" by
+-- setting the register to a dummy variable called "null", /not/ by setting to
+-- JS's nil value.
+resetResultVar :: StgRet -> JStat
+resetResultVar r = toJExpr r |= null_
+
+-- | Define closures based on size, these functions are syntactic sugar, e.g., a
+-- Haskell function which generates some useful JS. Each Closure constructor
+-- follows the naming convention h$cN, where N is a natural number. For example,
+-- h$c (with the nat omitted) is a JS Land Constructor for a closure in JS land
+-- which has a single entry function 'f', and no fields; identical to h$c0. h$c1
+-- is a JS Land Constructor for a closure with an entry function 'f', and a
+-- /single/ field 'x1', 'Just foo' is an example of this kind of closure. h$c2
+-- is a JS Land Constructor for a closure with an entry function and two data
+-- fields: 'x1' and 'x2'. And so on. Note that this has JIT performance
+-- implications; you should use h$c1, h$c2, h$c3, ... h$c24 instead of making
+-- objects manually so layouts and fields can be changed more easily and so the
+-- JIT can optimize better.
+closureConstructors :: StgToJSConfig -> JStat
+closureConstructors s = BlockStat
+ [ declClsConstr "h$c" ["f"] $ Closure
+ { clEntry = var "f"
+ , clField1 = null_
+ , clField2 = null_
+ , clMeta = 0
+ , clCC = ccVal
+ }
+ , declClsConstr "h$c0" ["f"] $ Closure
+ { clEntry = var "f"
+ , clField1 = null_
+ , clField2 = null_
+ , clMeta = 0
+ , clCC = ccVal
+ }
+ , declClsConstr "h$c1" ["f", "x1"] $ Closure
+ { clEntry = var "f"
+ , clField1 = var "x1"
+ , clField2 = null_
+ , clMeta = 0
+ , clCC = ccVal
+ }
+ , declClsConstr "h$c2" ["f", "x1", "x2"] $ Closure
+ { clEntry = var "f"
+ , clField1 = var "x1"
+ , clField2 = var "x2"
+ , clMeta = 0
+ , clCC = ccVal
+ }
+ , mconcat (map mkClosureCon [3..24])
+ , mconcat (map mkDataFill [1..24])
+ ]
+ where
+ prof = csProf s
+ (ccArg,ccVal)
+ -- the cc argument happens to be named just like the cc field...
+ | prof = ([TxtI closureCC_], Just (var closureCC_))
+ | otherwise = ([], Nothing)
+ addCCArg as = map TxtI as ++ ccArg
+ addCCArg' as = as ++ ccArg
+
+ declClsConstr i as cl = TxtI i ||= ValExpr (JFunc (addCCArg as)
+ ( jVar $ \x ->
+ [ checkC
+ , x |= newClosure cl
+ , notifyAlloc x
+ , traceAlloc x
+ , returnS x
+ ]
+ ))
+
+ traceAlloc x | csTraceRts s = appS "h$traceAlloc" [x]
+ | otherwise = mempty
+
+ notifyAlloc x | csDebugAlloc s = appS "h$debugAlloc_notifyAlloc" [x]
+ | otherwise = mempty
+
+ -- only JSVal can typically contain undefined or null
+ -- although it's possible (and legal) to make other Haskell types
+ -- to contain JS refs directly
+ -- this can cause false positives here
+ checkC :: JStat
+ checkC | csAssertRts s =
+ jVar $ \msg ->
+ jwhenS (var "arguments" .! 0 .!==. jString "h$baseZCGHCziJSziPrimziJSVal_con_e")
+ (loop 1 (.<. var "arguments" .^ "length")
+ (\i ->
+ mconcat [msg |= jString "warning: undefined or null in argument: "
+ + i
+ + jString " allocating closure: " + (var "arguments" .! 0 .^ "n")
+ , appS "h$log" [msg]
+ , jwhenS (var "console" .&&. (var "console" .^ "trace")) ((var "console" .^ "trace") `ApplStat` [msg])
+ , postIncrS i
+ ])
+
+ )
+ | otherwise = mempty
+
+ -- h$d is never used for JSVal (since it's only for constructors with
+ -- at least three fields, so we always warn here
+ checkD | csAssertRts s =
+ loop 0 (.<. var "arguments" .^ "length")
+ (\i -> jwhenS ((var "arguments" .! i .===. null_)
+ .||. (var "arguments" .! i .===. undefined_))
+ (jVar $ \msg ->
+ mconcat [ msg |= jString "warning: undefined or null in argument: " + i + jString " allocating fields"
+ , jwhenS (var "console" .&&. (var "console" .^ "trace"))
+ ((var "console" .^ "trace") `ApplStat` [msg])
+ ]))
+
+ | otherwise = mempty
+
+ mkClosureCon :: Int -> JStat
+ mkClosureCon n = funName ||= toJExpr fun
+ where
+ funName = TxtI $ mkFastString ("h$c" ++ show n)
+ -- args are: f x1 x2 .. xn [cc]
+ args = TxtI "f" : addCCArg' (map (TxtI . mkFastString . ('x':) . show) [(1::Int)..n])
+ fun = JFunc args funBod
+ -- x1 goes into closureField1. All the other args are bundled into an
+ -- object in closureField2: { d1 = x2, d2 = x3, ... }
+ --
+ extra_args = ValExpr . JHash . listToUniqMap $ zip
+ (map (mkFastString . ('d':) . show) [(1::Int)..])
+ (map (toJExpr . TxtI . mkFastString . ('x':) . show) [2..n])
+
+ funBod = jVar $ \x ->
+ [ checkC
+ , x |= newClosure Closure
+ { clEntry = var "f"
+ , clField1 = var "x1"
+ , clField2 = extra_args
+ , clMeta = 0
+ , clCC = ccVal
+ }
+ , notifyAlloc x
+ , traceAlloc x
+ , returnS x
+ ]
+
+ mkDataFill :: Int -> JStat
+ mkDataFill n = funName ||= toJExpr fun
+ where
+ funName = TxtI $ mkFastString ("h$d" ++ show n)
+ ds = map (mkFastString . ('d':) . show) [(1::Int)..n]
+ extra_args = ValExpr . JHash . listToUniqMap . zip ds $ map (toJExpr . TxtI) ds
+ fun = JFunc (map TxtI ds) (checkD <> returnS extra_args)
+
+-- | JS Payload to perform stack manipulation in the RTS
+stackManip :: JStat
+stackManip = mconcat (map mkPush [1..32]) <>
+ mconcat (map mkPpush [1..255])
+ where
+ mkPush :: Int -> JStat
+ mkPush n = let funName = TxtI $ mkFastString ("h$p" ++ show n)
+ as = map (TxtI . mkFastString . ('x':) . show) [1..n]
+ fun = JFunc as ((sp |= sp + toJExpr n)
+ <> mconcat (zipWith (\i a -> stack .! (sp - toJExpr (n-i)) |= toJExpr a)
+ [1..] as))
+ in funName ||= toJExpr fun
+
+ -- partial pushes, based on bitmap, increases Sp by highest bit
+ mkPpush :: Integer -> JStat
+ mkPpush sig | sig Bits..&. (sig+1) == 0 = mempty -- already handled by h$p
+ mkPpush sig = let funName = TxtI $ mkFastString ("h$pp" ++ show sig)
+ bits = bitsIdx sig
+ n = length bits
+ h = last bits
+ args = map (TxtI . mkFastString . ('x':) . show) [1..n]
+ fun = JFunc args $
+ mconcat [ sp |= sp + toJExpr (h+1)
+ , mconcat (zipWith (\b a -> stack .! (sp - toJExpr (h-b)) |= toJExpr a) bits args)
+ ]
+ in funName ||= toJExpr fun
+
+bitsIdx :: Integer -> [Int]
+bitsIdx n | n < 0 = error "bitsIdx: negative"
+ | otherwise = go n 0
+ where
+ go 0 _ = []
+ go m b | Bits.testBit m b = b : go (Bits.clearBit m b) (b+1)
+ | otherwise = go (Bits.clearBit m b) (b+1)
+
+bhLneStats :: StgToJSConfig -> JExpr -> JExpr -> JStat
+bhLneStats _s p frameSize =
+ jVar $ \v ->
+ mconcat [ v |= stack .! p
+ , ifS v
+ ((sp |= sp - frameSize)
+ <> ifS (v .===. var "h$blackhole")
+ (returnS $ app "h$throw" [var "h$baseZCControlziExceptionziBasezinonTermination", false_])
+ (mconcat [r1 |= v
+ , sp |= sp - frameSize
+ , returnStack
+ ]))
+ ((stack .! p |= var "h$blackhole") <> returnS null_)
+ ]
+
+
+-- | JS payload to declare the registers
+declRegs :: JStat
+declRegs =
+ mconcat [ TxtI "h$regs" ||= toJExpr (JList [])
+ , mconcat (map declReg (enumFromTo R1 R32))
+ , regGettersSetters
+ , loadRegs
+ ]
+ where
+ declReg r = (decl . TxtI . mkFastString . ("h$"++) . map toLower . show) r
+ <> BlockStat [AssignStat (toJExpr r) (ValExpr (JInt 0))] -- [j| `r` = 0; |]
+
+-- | JS payload to define getters and setters on the registers.
+regGettersSetters :: JStat
+regGettersSetters =
+ mconcat [ TxtI "h$getReg" ||= jLam (\n -> SwitchStat n getRegCases mempty)
+ , TxtI "h$setReg" ||= jLam (\n v -> SwitchStat n (setRegCases v) mempty)
+ ]
+ where
+ getRegCases =
+ map (\r -> (toJExpr (jsRegToInt r) , returnS (toJExpr r))) regsFromR1
+ setRegCases v =
+ map (\r -> (toJExpr (jsRegToInt r), (toJExpr r |= toJExpr v) <> returnS undefined_)) regsFromR1
+
+-- | JS payload that defines the functions to load each register
+loadRegs :: JStat
+loadRegs = mconcat $ map mkLoad [1..32]
+ where
+ mkLoad :: Int -> JStat
+ mkLoad n = let args = map (TxtI . mkFastString . ("x"++) . show) [1..n]
+ assign = zipWith (\a r -> toJExpr r |= toJExpr a)
+ args (reverse $ take n regsFromR1)
+ fname = TxtI $ mkFastString ("h$l" ++ show n)
+ fun = JFunc args (mconcat assign)
+ in fname ||= toJExpr fun
+
+-- | Assign registers R1 ... Rn in descending order, that is assign Rn first.
+-- This function uses the 'assignRegs'' array to construct functions which set
+-- the registers.
+assignRegs :: StgToJSConfig -> [JExpr] -> JStat
+assignRegs _ [] = mempty
+assignRegs s xs
+ | l <= 32 && not (csInlineLoadRegs s)
+ = ApplStat (ValExpr (JVar $ assignRegs'!l)) (reverse xs)
+ | otherwise = mconcat . reverse $
+ zipWith (\r ex -> toJExpr r |= ex) (take l regsFromR1) xs
+ where
+ l = length xs
+
+-- | JS payload which defines an array of function symbols that set N registers
+-- from M parameters. For example, h$l2 compiles to:
+-- @
+-- function h$l4(x1, x2, x3, x4) {
+-- h$r4 = x1;
+-- h$r3 = x2;
+-- h$r2 = x3;
+-- h$r1 = x4;
+-- };
+-- @
+assignRegs' :: Array Int Ident
+assignRegs' = listArray (1,32) (map (TxtI . mkFastString . ("h$l"++) . show) [(1::Int)..32])
+
+-- | JS payload to declare return variables.
+declRets :: JStat
+declRets = mconcat $ map (decl . TxtI . mkFastString . ("h$"++) . map toLower . show) (enumFrom Ret1)
+
+-- | JS payload defining the types closures.
+closureTypes :: JStat
+closureTypes = mconcat (map mkClosureType (enumFromTo minBound maxBound)) <> closureTypeName
+ where
+ mkClosureType :: ClosureType -> JStat
+ mkClosureType c = let s = TxtI . mkFastString $ "h$" ++ map toUpper (show c) ++ "_CLOSURE"
+ in s ||= toJExpr c
+ closureTypeName :: JStat
+ closureTypeName =
+ TxtI "h$closureTypeName" ||= jLam (\c ->
+ mconcat (map (ifCT c) [minBound..maxBound])
+ <> returnS (jString "InvalidClosureType"))
+
+ ifCT :: JExpr -> ClosureType -> JStat
+ ifCT arg ct = jwhenS (arg .===. toJExpr ct) (returnS (toJExpr (show ct)))
+
+-- | JS payload declaring the RTS functions.
+rtsDecls :: JStat
+rtsDecls = jsSaturate (Just "h$RTSD") $
+ mconcat [ TxtI "h$currentThread" ||= null_ -- thread state object for current thread
+ , TxtI "h$stack" ||= null_ -- stack for the current thread
+ , TxtI "h$sp" ||= 0 -- stack pointer for the current thread
+ , TxtI "h$initStatic" ||= toJExpr (JList []) -- we need delayed initialization for static objects, push functions here to be initialized just before haskell runs
+ , TxtI "h$staticThunks" ||= toJExpr (jhFromList []) -- funcName -> heapidx map for srefs
+ , TxtI "h$staticThunksArr" ||= toJExpr (JList []) -- indices of updatable thunks in static heap
+ , TxtI "h$CAFs" ||= toJExpr (JList [])
+ , TxtI "h$CAFsReset" ||= toJExpr (JList [])
+ -- stg registers
+ , declRegs
+ , declRets]
+
+-- | print the embedded RTS to a String
+rtsText :: StgToJSConfig -> String
+rtsText = show . pretty . rts
+
+-- | print the RTS declarations to a String.
+rtsDeclsText :: String
+rtsDeclsText = show . pretty $ rtsDecls
+
+-- | Wrapper over the RTS to guarentee saturation, see 'GHC.JS.Transform'
+rts :: StgToJSConfig -> JStat
+rts = jsSaturate (Just "h$RTS") . rts'
+
+-- | JS Payload which defines the embedded RTS.
+rts' :: StgToJSConfig -> JStat
+rts' s =
+ mconcat [ closureConstructors s
+ , garbageCollector
+ , stackManip
+ , TxtI "h$rts_traceForeign" ||= toJExpr (csTraceForeign s)
+ , TxtI "h$rts_profiling" ||= toJExpr (csProf s)
+ , TxtI "h$ct_fun" ||= toJExpr Fun
+ , TxtI "h$ct_con" ||= toJExpr Con
+ , TxtI "h$ct_thunk" ||= toJExpr Thunk
+ , TxtI "h$ct_pap" ||= toJExpr Pap
+ , TxtI "h$ct_blackhole" ||= toJExpr Blackhole
+ , TxtI "h$ct_stackframe" ||= toJExpr StackFrame
+ , TxtI "h$vt_ptr" ||= toJExpr PtrV
+ , TxtI "h$vt_void" ||= toJExpr VoidV
+ , TxtI "h$vt_double" ||= toJExpr IntV
+ , TxtI "h$vt_long" ||= toJExpr LongV
+ , TxtI "h$vt_addr" ||= toJExpr AddrV
+ , TxtI "h$vt_rtsobj" ||= toJExpr RtsObjV
+ , TxtI "h$vt_obj" ||= toJExpr ObjV
+ , TxtI "h$vt_arr" ||= toJExpr ArrV
+ , TxtI "h$bh" ||= jLam (bhStats s True)
+ , TxtI "h$bh_lne" ||= jLam (\x frameSize -> bhLneStats s x frameSize)
+ , closure (ClosureInfo (TxtI "h$blackhole") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIBlackhole mempty)
+ (appS "throw" [jString "oops: entered black hole"])
+ , closure (ClosureInfo (TxtI "h$blackholeTrap") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIThunk mempty)
+ (appS "throw" [jString "oops: entered multiple times"])
+ , closure (ClosureInfo (TxtI "h$done") (CIRegs 0 [PtrV]) "done" (CILayoutUnknown 0) CIStackFrame mempty)
+ (appS "h$finishThread" [var "h$currentThread"] <> returnS (var "h$reschedule"))
+ , closure (ClosureInfo (TxtI "h$doneMain_e") (CIRegs 0 [PtrV]) "doneMain" (CILayoutUnknown 0) CIStackFrame mempty)
+ (returnS (var "h$doneMain"))
+ , conClosure (TxtI "h$false_e") "GHC.Types.False" (CILayoutFixed 0 []) 1
+ , conClosure (TxtI "h$true_e" ) "GHC.Types.True" (CILayoutFixed 0 []) 2
+ -- generic data constructor with 1 non-heapobj field
+ , conClosure (TxtI "h$data1_e") "data1" (CILayoutFixed 1 [ObjV]) 1
+ -- generic data constructor with 2 non-heapobj fields
+ , conClosure (TxtI "h$data2_e") "data2" (CILayoutFixed 2 [ObjV,ObjV]) 1
+ , closure (ClosureInfo (TxtI "h$noop_e") (CIRegs 1 [PtrV]) "no-op IO ()" (CILayoutFixed 0 []) (CIFun 1 0) mempty)
+ (returnS (stack .! sp))
+ <> (TxtI "h$noop" ||= ApplExpr (var "h$c0") (var "h$noop_e" : [jSystemCCS | csProf s]))
+ , closure (ClosureInfo (TxtI "h$catch_e") (CIRegs 0 [PtrV]) "exception handler" (CILayoutFixed 2 [PtrV,IntV]) CIStackFrame mempty)
+ (adjSpN' 3 <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$dataToTag_e") (CIRegs 0 [PtrV]) "data to tag" (CILayoutFixed 0 []) CIStackFrame mempty)
+ $ mconcat [ r1 |= if_ (r1 .===. true_) 1 (if_ (typeof r1 .===. jTyObject) (r1 .^ "f" .^ "a" - 1) 0)
+ , adjSpN' 1
+ , returnS (stack .! sp)
+ ]
+ -- function application to one argument
+ , closure (ClosureInfo (TxtI "h$ap1_e") (CIRegs 0 [PtrV]) "apply1" (CILayoutFixed 2 [PtrV, PtrV]) CIThunk mempty)
+ (jVar $ \d1 d2 ->
+ mconcat [ d1 |= closureField1 r1
+ , d2 |= closureField2 r1
+ , appS "h$bh" []
+ , profStat s enterCostCentreThunk
+ , r1 |= d1
+ , r2 |= d2
+ , returnS (app "h$ap_1_1_fast" [])
+ ])
+ -- function application to two arguments
+ , closure (ClosureInfo (TxtI "h$ap2_e") (CIRegs 0 [PtrV]) "apply2" (CILayoutFixed 3 [PtrV, PtrV, PtrV]) CIThunk mempty)
+ (jVar $ \d1 d2 d3 ->
+ mconcat [ d1 |= closureField1 r1
+ , d2 |= closureField2 r1 .^ "d1"
+ , d3 |= closureField2 r1 .^ "d2"
+ , appS "h$bh" []
+ , profStat s enterCostCentreThunk
+ , r1 |= d1
+ , r2 |= d2
+ , r3 |= d3
+ , returnS (app "h$ap_2_2_fast" [])
+ ])
+ -- function application to three arguments
+ , closure (ClosureInfo (TxtI "h$ap3_e") (CIRegs 0 [PtrV]) "apply3" (CILayoutFixed 4 [PtrV, PtrV, PtrV, PtrV]) CIThunk mempty)
+ (jVar $ \d1 d2 d3 d4 ->
+ mconcat [ d1 |= closureField1 r1
+ , d2 |= closureField2 r1 .^ "d1"
+ , d3 |= closureField2 r1 .^ "d2"
+ , d4 |= closureField2 r1 .^ "d3"
+ , appS "h$bh" []
+ , r1 |= d1
+ , r2 |= d2
+ , r3 |= d3
+ , r4 |= d4
+ , returnS (app "h$ap_3_3_fast" [])
+ ])
+ -- select first field
+ , closure (ClosureInfo (TxtI "h$select1_e") (CIRegs 0 [PtrV]) "select1" (CILayoutFixed 1 [PtrV]) CIThunk mempty)
+ (jVar $ \t ->
+ mconcat [ t |= closureField1 r1
+ , adjSp' 3
+ , stack .! (sp - 2) |= r1
+ , stack .! (sp - 1) |= var "h$upd_frame"
+ , stack .! sp |= var "h$select1_ret"
+ , closureEntry r1 |= var "h$blackhole"
+ , closureField1 r1 |= var "h$currentThread"
+ , closureField2 r1 |= null_
+ , r1 |= t
+ , returnS (app "h$ap_0_0_fast" [])
+ ])
+ , closure (ClosureInfo (TxtI "h$select1_ret") (CIRegs 0 [PtrV]) "select1ret" (CILayoutFixed 0 []) CIStackFrame mempty)
+ ((r1 |= closureField1 r1)
+ <> adjSpN' 1
+ <> returnS (app "h$ap_0_0_fast" [])
+ )
+ -- select second field of a two-field constructor
+ , closure (ClosureInfo (TxtI "h$select2_e") (CIRegs 0 [PtrV]) "select2" (CILayoutFixed 1 [PtrV]) CIThunk mempty)
+ (jVar $ \t ->
+ mconcat [t |= closureField1 r1
+ , adjSp' 3
+ , stack .! (sp - 2) |= r1
+ , stack .! (sp - 1) |= var "h$upd_frame"
+ , stack .! sp |= var "h$select2_ret"
+ , closureEntry r1 |= var "h$blackhole"
+ , closureField1 r1 |= var "h$currentThread"
+ , closureField2 r1 |= null_
+ , r1 |= t
+ , returnS (app "h$ap_0_0_fast" [])
+ ]
+ )
+ , closure (ClosureInfo (TxtI "h$select2_ret") (CIRegs 0 [PtrV]) "select2ret" (CILayoutFixed 0 []) CIStackFrame mempty)
+ $ mconcat [ r1 |= closureField2 r1
+ , adjSpN' 1
+ , returnS (app "h$ap_0_0_fast" [])
+ ]
+ , closure (ClosureInfo (TxtI "h$keepAlive_e") (CIRegs 0 [PtrV]) "keepAlive" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ (mconcat [ adjSpN' 2
+ , returnS (stack .! sp)
+ ]
+ )
+ -- a thunk that just raises a synchronous exception
+ , closure (ClosureInfo (TxtI "h$raise_e") (CIRegs 0 [PtrV]) "h$raise_e" (CILayoutFixed 0 []) CIThunk mempty)
+ (returnS (app "h$throw" [closureField1 r1, false_]))
+ , closure (ClosureInfo (TxtI "h$raiseAsync_e") (CIRegs 0 [PtrV]) "h$raiseAsync_e" (CILayoutFixed 0 []) CIThunk mempty)
+ (returnS (app "h$throw" [closureField1 r1, true_]))
+ , closure (ClosureInfo (TxtI "h$raiseAsync_frame") (CIRegs 0 []) "h$raiseAsync_frame" (CILayoutFixed 1 []) CIStackFrame mempty)
+ (jVar $ \ex ->
+ mconcat [ ex |= stack .! (sp - 1)
+ , adjSpN' 2
+ , returnS (app "h$throw" [ex, true_])
+ ])
+ {- reduce result if it's a thunk, follow if it's an ind
+ add this to the stack if you want the outermost result
+ to always be reduced to whnf, and not an ind
+ -}
+ , closure (ClosureInfo (TxtI "h$reduce") (CIRegs 0 [PtrV]) "h$reduce" (CILayoutFixed 0 []) CIStackFrame mempty)
+ (ifS (isThunk r1)
+ (returnS (r1 .^ "f"))
+ (adjSpN' 1 <> returnS (stack .! sp))
+ )
+ , rtsApply s
+ , closureTypes
+ , closure (ClosureInfo (TxtI "h$runio_e") (CIRegs 0 [PtrV]) "runio" (CILayoutFixed 1 [PtrV]) CIThunk mempty)
+ $ mconcat [ r1 |= closureField1 r1
+ , stack .! PreInc sp |= var "h$ap_1_0"
+ , returnS (var "h$ap_1_0")
+ ]
+ , closure (ClosureInfo (TxtI "h$flushStdout_e") (CIRegs 0 []) "flushStdout" (CILayoutFixed 0 []) CIThunk mempty)
+ $ mconcat [ r1 |= var "h$baseZCGHCziIOziHandlezihFlush"
+ , r2 |= var "h$baseZCGHCziIOziHandleziFDzistdout"
+ , returnS (app "h$ap_1_1_fast" [])
+ ]
+ , TxtI "h$flushStdout" ||= app "h$static_thunk" [var "h$flushStdout_e"]
+ -- the scheduler pushes this frame when suspending a thread that
+ -- has not called h$reschedule explicitly
+ , closure (ClosureInfo (TxtI "h$restoreThread") (CIRegs 0 []) "restoreThread" CILayoutVariable CIStackFrame mempty)
+ (jVar $ \f frameSize nregs ->
+ mconcat [f |= stack .! (sp - 2)
+ , frameSize |= stack .! (sp - 1)
+ , nregs |= frameSize - 3
+ , loop 1 (.<=. nregs)
+ (\i -> appS "h$setReg" [i, stack .! (sp - 2 - i)] <> postIncrS i)
+ , sp |= sp - frameSize
+ , returnS f
+ ])
+ -- return a closure in the stack frame to the next thing on the stack
+ , closure (ClosureInfo (TxtI "h$return") (CIRegs 0 []) "return" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ ((r1 |= stack .! (sp - 1))
+ <> adjSpN' 2
+ <> returnS (stack .! sp))
+ -- return a function in the stack frame for the next call
+ , closure (ClosureInfo (TxtI "h$returnf") (CIRegs 0 [PtrV]) "returnf" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)
+ (jVar $ \r ->
+ mconcat [ r |= stack .! (sp - 1)
+ , adjSpN' 2
+ , returnS r
+ ])
+ -- return this function when the scheduler needs to come into action
+ -- (yield, delay etc), returning thread needs to push all relevant
+ -- registers to stack frame, thread will be resumed by calling the stack top
+ , closure (ClosureInfo (TxtI "h$reschedule") (CIRegs 0 []) "reschedule" (CILayoutFixed 0 []) CIThunk mempty)
+ (returnS $ var "h$reschedule")
+ -- debug thing, insert on stack to dump current result, should be boxed
+ , closure (ClosureInfo (TxtI "h$dumpRes") (CIRegs 0 [PtrV]) "dumpRes" (CILayoutFixed 1 [ObjV]) CIThunk mempty)
+ (jVar $ \re ->
+ mconcat [ appS "h$log" [jString "h$dumpRes result: " + stack .! (sp-1)]
+ , appS "h$log" [r1]
+ , appS "h$log" [app "h$collectProps" [r1]]
+ , jwhenS ((r1 .^ "f") .&&. (r1 .^ "f" .^ "n"))
+ (appS "h$log" [jString "name: " + r1 .^ "f" .^ "n"])
+ , jwhenS (ApplExpr (r1 .^ "hasOwnProperty") [jString closureField1_])
+ (appS "h$log" [jString "d1: " + closureField1 r1])
+ , jwhenS (ApplExpr (r1 .^ "hasOwnProperty") [jString closureField2_])
+ (appS "h$log" [jString "d2: " + closureField2 r1])
+ , jwhenS (r1 .^ "f") $ mconcat
+ [ re |= New (app "RegExp" [jString "([^\\n]+)\\n(.|\\n)*"])
+ , appS "h$log" [jString "function"
+ + ApplExpr (ApplExpr ((jString "" + r1 .^ "f") .^ "substring") [0, 50] .^ "replace") [r1, jString "$1"]]
+ ]
+ , adjSpN' 2
+ , r1 |= null_
+ , returnS (stack .! sp)
+ ])
+ , closure (ClosureInfo (TxtI "h$resume_e") (CIRegs 0 [PtrV]) "resume" (CILayoutFixed 0 []) CIThunk mempty)
+ (jVar $ \ss ->
+ mconcat [ss |= closureField1 r1
+ , updateThunk' s
+ , loop 0 (.<. ss .^ "length") (\i -> (stack .! (sp+1+i) |= ss .! i)
+ <> postIncrS i)
+ , sp |= sp + ss .^ "length"
+ , r1 |= null_
+ , returnS (stack .! sp)
+ ])
+ , closure (ClosureInfo (TxtI "h$unmaskFrame") (CIRegs 0 [PtrV]) "unmask" (CILayoutFixed 0 []) CIStackFrame mempty)
+ ((var "h$currentThread" .^ "mask" |= 0)
+ <> adjSpN' 1
+ -- back to scheduler to give us async exception if pending
+ <> ifS (var "h$currentThread" .^ "excep" .^ "length" .>. 0)
+ (push' s [r1, var "h$return"] <> returnS (var "h$reschedule"))
+ (returnS (stack .! sp)))
+ , closure (ClosureInfo (TxtI "h$maskFrame") (CIRegs 0 [PtrV]) "mask" (CILayoutFixed 0 []) CIStackFrame mempty)
+ ((var "h$currentThread" .^ "mask" |= 2)
+ <> adjSpN' 1
+ <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$maskUnintFrame") (CIRegs 0 [PtrV]) "maskUnint" (CILayoutFixed 0 []) CIStackFrame mempty)
+ ((var "h$currentThread" .^ "mask" |= 1)
+ <> adjSpN' 1
+ <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$unboxFFIResult") (CIRegs 0 [PtrV]) "unboxFFI" (CILayoutFixed 0 []) CIStackFrame mempty)
+ (jVar $ \d ->
+ mconcat [d |= closureField1 r1
+ , loop 0 (.<. d .^ "length") (\i -> appS "h$setReg" [i + 1, d .! i] <> postIncrS i)
+ , adjSpN' 1
+ , returnS (stack .! sp)
+ ])
+ , closure (ClosureInfo (TxtI "h$unbox_e") (CIRegs 0 [PtrV]) "unboxed value" (CILayoutFixed 1 [DoubleV]) CIThunk mempty)
+ ((r1 |= closureField1 r1) <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$retryInterrupted") (CIRegs 0 [ObjV]) "retry interrupted operation" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)
+ (jVar $ \a ->
+ mconcat [ a |= stack .! (sp - 1)
+ , adjSpN' 2
+ , returnS (ApplExpr (a .! 0 .^ "apply") [var "this", ApplExpr (a .^ "slice") [1]])
+ ])
+ , closure (ClosureInfo (TxtI "h$atomically_e") (CIRegs 0 [PtrV]) "atomic operation" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ (ifS (app "h$stmValidateTransaction" [])
+ (appS "h$stmCommitTransaction" []
+ <> adjSpN' 2
+ <> returnS (stack .! sp))
+ (returnS (app "h$stmStartTransaction" [stack .! (sp - 1)])))
+
+ , closure (ClosureInfo (TxtI "h$stmCatchRetry_e") (CIRegs 0 [PtrV]) "catch retry" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)
+ (adjSpN' 2
+ <> appS "h$stmCommitTransaction" []
+ <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$catchStm_e") (CIRegs 0 [PtrV]) "STM catch" (CILayoutFixed 3 [ObjV,PtrV,ObjV]) CIStackFrame mempty)
+ (adjSpN' 4
+ <> appS "h$stmCommitTransaction" []
+ <> returnS (stack .! sp))
+ , closure (ClosureInfo (TxtI "h$stmResumeRetry_e") (CIRegs 0 [PtrV]) "resume retry" (CILayoutFixed 0 []) CIStackFrame mempty)
+ (jVar $ \blocked ->
+ mconcat [ jwhenS (stack .! (sp - 2) .!==. var "h$atomically_e")
+ (appS "throw" [jString "h$stmResumeRetry_e: unexpected value on stack"])
+ , blocked |= stack .! (sp - 1)
+ , adjSpN' 2
+ , appS "h$stmRemoveBlockedThread" [blocked, var "h$currentThread"]
+ , returnS (app "h$stmStartTransaction" [stack .! (sp - 1)])
+ ])
+ , closure (ClosureInfo (TxtI "h$lazy_e") (CIRegs 0 [PtrV]) "generic lazy value" (CILayoutFixed 0 []) CIThunk mempty)
+ (jVar $ \x ->
+ mconcat [x |= ApplExpr (closureField1 r1) []
+ , appS "h$bh" []
+ , profStat s enterCostCentreThunk
+ , r1 |= x
+ , returnS (stack .! sp)
+ ])
+ -- Top-level statements to generate only in profiling mode
+ , profStat s (closure (ClosureInfo (TxtI "h$setCcs_e") (CIRegs 0 [PtrV]) "set cost centre stack" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)
+ (appS "h$restoreCCS" [ stack .! (sp - 1)]
+ <> adjSpN' 2
+ <> returnS (stack .! sp)))
+ ]
diff --git a/compiler/GHC/StgToJS/Rts/Types.hs b/compiler/GHC/StgToJS/Rts/Types.hs
new file mode 100644
index 0000000000..f1a0276d5d
--- /dev/null
+++ b/compiler/GHC/StgToJS/Rts/Types.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE CPP,
+ FlexibleInstances,
+ OverloadedStrings #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Rts.Apply
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Types and utility functions used in the JS RTS.
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Rts.Types where
+
+import GHC.Prelude
+
+import GHC.JS.Make
+import GHC.JS.Syntax
+import GHC.StgToJS.Regs
+import GHC.StgToJS.Types
+
+--------------------------------------------------------------------------------
+-- Syntactic Sugar for some Utilities we want in JS land
+--------------------------------------------------------------------------------
+
+-- | Syntactic sugar, i.e., a Haskell function which generates useful JS code.
+-- Given a @JExpr@, 'ex', inject a trace statement on 'ex' in the compiled JS
+-- program
+traceRts :: StgToJSConfig -> JExpr -> JStat
+traceRts s ex | (csTraceRts s) = appS "h$log" [ex]
+ | otherwise = mempty
+
+-- | Syntactic sugar. Given a @JExpr@, 'ex' which is assumed to be a predicate,
+-- and a message 'm', assert that 'not ex' is True, if not throw an exception in
+-- JS land with message 'm'.
+assertRts :: ToJExpr a => StgToJSConfig -> JExpr -> a -> JStat
+assertRts s ex m | csAssertRts s = jwhenS (UOpExpr NotOp ex) (appS "throw" [toJExpr m])
+ | otherwise = mempty
+
+-- | name of the closure 'c'
+clName :: JExpr -> JExpr
+clName c = c .^ "n"
+
+-- | Type name of the closure 'c'
+clTypeName :: JExpr -> JExpr
+clTypeName c = app "h$closureTypeName" [c .^ "t"]
+
+-- number of arguments (arity & 0xff = arguments, arity >> 8 = number of registers)
+stackFrameSize :: JExpr -- ^ assign frame size to this
+ -> JExpr -- ^ stack frame header function
+ -> JStat -- ^ size of the frame, including header
+stackFrameSize tgt f =
+ ifS (f .===. var "h$ap_gen") -- h$ap_gen is special
+ (tgt |= (stack .! (sp - 1) .>>. 8) + 2)
+ (jVar (\tag ->
+ mconcat
+ [tag |= f .^ "size"
+ , ifS (tag .<. 0) -- if tag is less than 0
+ (tgt |= stack .! (sp - 1)) -- set target to stack pointer - 1
+ (tgt |= mask8 tag + 1) -- else set to mask'd tag + 1
+ ]
+ ))
+
+--------------------------------------------------------------------------------
+-- Register utilities
+--------------------------------------------------------------------------------
+
+-- | Perform the computation 'f', on the range of registers bounded by 'start'
+-- and 'end'.
+withRegs :: StgReg -> StgReg -> (StgReg -> JStat) -> JStat
+withRegs start end f = mconcat $ fmap f [start..end]
diff --git a/compiler/GHC/StgToJS/Sinker.hs b/compiler/GHC/StgToJS/Sinker.hs
new file mode 100644
index 0000000000..6df58d4fcf
--- /dev/null
+++ b/compiler/GHC/StgToJS/Sinker.hs
@@ -0,0 +1,180 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE LambdaCase #-}
+
+module GHC.StgToJS.Sinker (sinkPgm) where
+
+import GHC.Prelude
+import GHC.Types.Unique.Set
+import GHC.Types.Unique.FM
+import GHC.Types.Var.Set
+import GHC.Stg.Syntax
+import GHC.Types.Id
+import GHC.Types.Name
+import GHC.Unit.Module
+import GHC.Types.Literal
+import GHC.Data.Graph.Directed
+
+import GHC.StgToJS.CoreUtils
+
+import Data.Char
+import Data.Either
+import Data.List (partition)
+import Data.Maybe
+
+
+-- | Unfloat some top-level unexported things
+--
+-- GHC floats constants to the top level. This is fine in native code, but with JS
+-- they occupy some global variable name. We can unfloat some unexported things:
+--
+-- - global constructors, as long as they're referenced only once by another global
+-- constructor and are not in a recursive binding group
+-- - literals (small literals may also be sunk if they are used more than once)
+sinkPgm :: Module
+ -> [CgStgTopBinding]
+ -> (UniqFM Id CgStgExpr, [CgStgTopBinding])
+sinkPgm m pgm = (sunk, map StgTopLifted pgm'' ++ stringLits)
+ where
+ selectLifted (StgTopLifted b) = Left b
+ selectLifted x = Right x
+ (pgm', stringLits) = partitionEithers (map selectLifted pgm)
+ (sunk, pgm'') = sinkPgm' m pgm'
+
+sinkPgm'
+ :: Module
+ -- ^ the module, since we treat definitions from the current module
+ -- differently
+ -> [CgStgBinding]
+ -- ^ the bindings
+ -> (UniqFM Id CgStgExpr, [CgStgBinding])
+ -- ^ a map with sunken replacements for nodes, for where the replacement
+ -- does not fit in the 'StgBinding' AST and the new bindings
+sinkPgm' m pgm =
+ let usedOnce = collectUsedOnce pgm
+ sinkables = listToUFM $
+ concatMap alwaysSinkable pgm ++
+ filter ((`elementOfUniqSet` usedOnce) . fst) (concatMap (onceSinkable m) pgm)
+ isSunkBind (StgNonRec b _e) | elemUFM b sinkables = True
+ isSunkBind _ = False
+ in (sinkables, filter (not . isSunkBind) $ topSortDecls m pgm)
+
+-- | always sinkable, values that may be duplicated in the generated code (e.g.
+-- small literals)
+alwaysSinkable :: CgStgBinding -> [(Id, CgStgExpr)]
+alwaysSinkable (StgRec {}) = []
+alwaysSinkable (StgNonRec b rhs) = case rhs of
+ StgRhsClosure _ _ _ _ e@(StgLit l)
+ | isSmallSinkableLit l
+ , isLocal b
+ -> [(b,e)]
+ StgRhsCon _ccs dc cnum _ticks as@[StgLitArg l]
+ | isSmallSinkableLit l
+ , isLocal b
+ , isUnboxableCon dc
+ -> [(b,StgConApp dc cnum as [])]
+ _ -> []
+
+isSmallSinkableLit :: Literal -> Bool
+isSmallSinkableLit (LitChar c) = ord c < 100000
+isSmallSinkableLit (LitNumber _ i) = abs i < 100000
+isSmallSinkableLit _ = False
+
+
+-- | once sinkable: may be sunk, but duplication is not ok
+onceSinkable :: Module -> CgStgBinding -> [(Id, CgStgExpr)]
+onceSinkable _m (StgNonRec b rhs)
+ | Just e <- getSinkable rhs
+ , isLocal b = [(b,e)]
+ where
+ getSinkable = \case
+ StgRhsCon _ccs dc cnum _ticks args -> Just (StgConApp dc cnum args [])
+ StgRhsClosure _ _ _ _ e@(StgLit{}) -> Just e
+ _ -> Nothing
+onceSinkable _ _ = []
+
+-- | collect all idents used only once in an argument at the top level
+-- and never anywhere else
+collectUsedOnce :: [CgStgBinding] -> IdSet
+collectUsedOnce binds = intersectUniqSets (usedOnce args) (usedOnce top_args)
+ where
+ top_args = concatMap collectArgsTop binds
+ args = concatMap collectArgs binds
+ usedOnce = fst . foldr g (emptyUniqSet, emptyUniqSet)
+ g i t@(once, mult)
+ | i `elementOfUniqSet` mult = t
+ | i `elementOfUniqSet` once
+ = (delOneFromUniqSet once i, addOneToUniqSet mult i)
+ | otherwise = (addOneToUniqSet once i, mult)
+
+-- | fold over all id in StgArg used at the top level in an StgRhsCon
+collectArgsTop :: CgStgBinding -> [Id]
+collectArgsTop = \case
+ StgNonRec _b r -> collectArgsTopRhs r
+ StgRec bs -> concatMap (collectArgsTopRhs . snd) bs
+
+collectArgsTopRhs :: CgStgRhs -> [Id]
+collectArgsTopRhs = \case
+ StgRhsCon _ccs _dc _mu _ticks args -> concatMap collectArgsA args
+ StgRhsClosure {} -> []
+
+-- | fold over all Id in StgArg in the AST
+collectArgs :: CgStgBinding -> [Id]
+collectArgs = \case
+ StgNonRec _b r -> collectArgsR r
+ StgRec bs -> concatMap (collectArgsR . snd) bs
+
+collectArgsR :: CgStgRhs -> [Id]
+collectArgsR = \case
+ StgRhsClosure _x0 _x1 _x2 _x3 e -> collectArgsE e
+ StgRhsCon _ccs _con _mu _ticks args -> concatMap collectArgsA args
+
+collectArgsAlt :: CgStgAlt -> [Id]
+collectArgsAlt alt = collectArgsE (alt_rhs alt)
+
+collectArgsE :: CgStgExpr -> [Id]
+collectArgsE = \case
+ StgApp x args
+ -> x : concatMap collectArgsA args
+ StgConApp _con _mn args _ts
+ -> concatMap collectArgsA args
+ StgOpApp _x args _t
+ -> concatMap collectArgsA args
+ StgCase e _b _a alts
+ -> collectArgsE e ++ concatMap collectArgsAlt alts
+ StgLet _x b e
+ -> collectArgs b ++ collectArgsE e
+ StgLetNoEscape _x b e
+ -> collectArgs b ++ collectArgsE e
+ StgTick _i e
+ -> collectArgsE e
+ StgLit _
+ -> []
+
+collectArgsA :: StgArg -> [Id]
+collectArgsA = \case
+ StgVarArg i -> [i]
+ StgLitArg _ -> []
+
+isLocal :: Id -> Bool
+isLocal i = isNothing (nameModule_maybe . idName $ i) && not (isExportedId i)
+
+-- | since we have sequential initialization, topsort the non-recursive
+-- constructor bindings
+topSortDecls :: Module -> [CgStgBinding] -> [CgStgBinding]
+topSortDecls _m binds = rest ++ nr'
+ where
+ (nr, rest) = partition isNonRec binds
+ isNonRec StgNonRec{} = True
+ isNonRec _ = False
+ vs = map getV nr
+ keys = mkUniqSet (map node_key vs)
+ getV e@(StgNonRec b _) = DigraphNode e b []
+ getV _ = error "topSortDecls: getV, unexpected binding"
+ collectDeps (StgNonRec b (StgRhsCon _cc _dc _cnum _ticks args)) =
+ [ (i, b) | StgVarArg i <- args, i `elementOfUniqSet` keys ]
+ collectDeps _ = []
+ g = graphFromVerticesAndAdjacency vs (concatMap collectDeps nr)
+ nr' | (not . null) [()| CyclicSCC _ <- stronglyConnCompG g]
+ = error "topSortDecls: unexpected cycle"
+ | otherwise = map node_payload (topologicalSortG g)
diff --git a/compiler/GHC/StgToJS/Stack.hs b/compiler/GHC/StgToJS/Stack.hs
new file mode 100644
index 0000000000..0250837f32
--- /dev/null
+++ b/compiler/GHC/StgToJS/Stack.hs
@@ -0,0 +1,373 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.JS.Stack
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+-- Utilities and wrappers for Stack manipulation in JS Land.
+--
+-- In general, functions suffixed with a tick do the actual work, functions
+-- suffixed with an "I" are identical to the non-I versions but work on 'Ident's
+--
+-- The stack in JS land is held in the special JS array 'h$stack' and the stack
+-- pointer is held in 'h$sp'. The top of the stack thus exists at
+-- 'h$stack[h$sp]'. h$stack[h$sp + i] where i > 0, moves deeper into the stack
+-- into older entries, whereas h$stack[h$sp - i] moves towards the top of the
+-- stack.
+--
+-- The stack layout algorithm is slightly peculiar. It makes an effort to
+-- remember recently popped things so that if these values need to be pushed
+-- then they can be quickly. The implementation for this is storing these values
+-- above the stack pointer, and the pushing will skip slots that we know we will
+-- use and fill in slots marked as unknown. Thus, you may find that our push and
+-- pop functions do some non-traditional stack manipulation such as adding slots
+-- in pop or removing slots in push.
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Stack
+ ( resetSlots
+ , isolateSlots
+ , setSlots
+ , getSlots
+ , addSlots
+ , dropSlots
+ , addUnknownSlots
+ , push
+ , push'
+ , adjSpN
+ , adjSpN'
+ , adjSp'
+ , adjSp
+ , pushNN
+ , pushNN'
+ , pushN'
+ , pushN
+ , pushOptimized'
+ , pushOptimized
+ , pushLneFrame
+ , popN
+ , popSkip
+ , popSkipI
+ , loadSkip
+ -- * Thunk update
+ , updateThunk
+ , updateThunk'
+ , bhStats
+ )
+where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Types
+import GHC.StgToJS.Monad
+import GHC.StgToJS.Ids
+import GHC.StgToJS.ExprCtx
+import GHC.StgToJS.Heap
+import GHC.StgToJS.Regs
+
+import GHC.Types.Id
+import GHC.Utils.Misc
+import GHC.Data.FastString
+
+import qualified Data.Bits as Bits
+import qualified Data.List as L
+import qualified Control.Monad.Trans.State.Strict as State
+import Data.Array
+import Data.Monoid
+import Control.Monad
+
+-- | Run the action, 'm', with no stack info
+resetSlots :: G a -> G a
+resetSlots m = do
+ s <- getSlots
+ d <- getStackDepth
+ setSlots []
+ a <- m
+ setSlots s
+ setStackDepth d
+ return a
+
+-- | run the action, 'm', with current stack info, but don't let modifications
+-- propagate
+isolateSlots :: G a -> G a
+isolateSlots m = do
+ s <- getSlots
+ d <- getStackDepth
+ a <- m
+ setSlots s
+ setStackDepth d
+ pure a
+
+-- | Set stack depth
+setStackDepth :: Int -> G ()
+setStackDepth d = modifyGroup (\s -> s { ggsStackDepth = d})
+
+-- | Get stack depth
+getStackDepth :: G Int
+getStackDepth = State.gets (ggsStackDepth . gsGroup)
+
+-- | Modify stack depth
+modifyStackDepth :: (Int -> Int) -> G ()
+modifyStackDepth f = modifyGroup (\s -> s { ggsStackDepth = f (ggsStackDepth s) })
+
+-- | overwrite our stack knowledge
+setSlots :: [StackSlot] -> G ()
+setSlots xs = modifyGroup (\g -> g { ggsStack = xs})
+
+-- | retrieve our current stack knowledge
+getSlots :: G [StackSlot]
+getSlots = State.gets (ggsStack . gsGroup)
+
+-- | Modify stack slots
+modifySlots :: ([StackSlot] -> [StackSlot]) -> G ()
+modifySlots f = modifyGroup (\g -> g { ggsStack = f (ggsStack g)})
+
+-- | add `n` unknown slots to our stack knowledge
+addUnknownSlots :: Int -> G ()
+addUnknownSlots n = addSlots (replicate n SlotUnknown)
+
+-- | add knowledge about the stack slots
+addSlots :: [StackSlot] -> G ()
+addSlots xs = do
+ s <- getSlots
+ setSlots (xs ++ s)
+
+-- | drop 'n' slots from our stack knowledge
+dropSlots :: Int -> G ()
+dropSlots n = modifySlots (drop n)
+
+push :: [JExpr] -> G JStat
+push xs = do
+ dropSlots (length xs)
+ modifyStackDepth (+ (length xs))
+ flip push' xs <$> getSettings
+
+push' :: StgToJSConfig -> [JExpr] -> JStat
+push' _ [] = mempty
+push' cs xs
+ | csInlinePush cs || l > 32 || l < 2 = adjSp' l <> mconcat items
+ | otherwise = ApplStat (toJExpr $ pushN ! l) xs
+ where
+ items = zipWith f [(1::Int)..] xs
+ offset i | i == l = sp
+ | otherwise = InfixExpr SubOp sp (toJExpr (l - i))
+ l = length xs
+ f i e = AssignStat ((IdxExpr stack) (toJExpr (offset i))) (toJExpr e)
+
+
+-- | Grow the stack pointer by 'n' without modifying the stack depth. The stack
+-- is just a JS array so we add to grow (instead of the traditional subtract)
+adjSp' :: Int -> JStat
+adjSp' 0 = mempty
+adjSp' n = sp |= InfixExpr AddOp sp (toJExpr n)
+
+-- | Shrink the stack pointer by 'n'. The stack grows downward so substract
+adjSpN' :: Int -> JStat
+adjSpN' 0 = mempty
+adjSpN' n = sp |= InfixExpr SubOp sp (toJExpr n)
+
+-- | Wrapper which adjusts the stack pointer /and/ modifies the stack depth
+-- tracked in 'G'. See also 'adjSp'' which actually does the stack pointer
+-- manipulation.
+adjSp :: Int -> G JStat
+adjSp 0 = return mempty
+adjSp n = do
+ -- grow depth by n
+ modifyStackDepth (+n)
+ return (adjSp' n)
+
+-- | Shrink the stack and stack pointer. NB: This function is unsafe when the
+-- input 'n', is negative. This function wraps around 'adjSpN' which actually
+-- performs the work.
+adjSpN :: Int -> G JStat
+adjSpN 0 = return mempty
+adjSpN n = do
+ modifyStackDepth (\x -> x - n)
+ return (adjSpN' n)
+
+-- | A constant array that holds global function symbols which do N pushes onto
+-- the stack. For example:
+-- @
+-- function h$p1(x1) {
+-- ++h$sp;
+-- h$stack[(h$sp - 0)] = x1;
+-- };
+-- function h$p2(x1, x2) {
+-- h$sp += 2;
+-- h$stack[(h$sp - 1)] = x1;
+-- h$stack[(h$sp - 0)] = x2;
+-- };
+-- @
+--
+-- and so on up to 32.
+pushN :: Array Int Ident
+pushN = listArray (1,32) $ map (TxtI . mkFastString . ("h$p"++) . show) [(1::Int)..32]
+
+-- | Convert all function symbols in 'pushN' to global top-level functions. This
+-- is a hack which converts the function symbols to variables. This hack is
+-- caught in 'GHC.StgToJS.Printer.prettyBlock'' to turn these into global
+-- functions.
+pushN' :: Array Int JExpr
+pushN' = fmap (ValExpr . JVar) pushN
+
+-- | Partial Push functions. Like 'pushN' except these push functions skip
+-- slots. For example,
+-- @
+-- function h$pp33(x1, x2) {
+-- h$sp += 6;
+-- h$stack[(h$sp - 5)] = x1;
+-- h$stack[(h$sp - 0)] = x2;
+-- };
+-- @
+--
+-- The 33rd entry skips slots 1-4 to bind the top of the stack and the 6th
+-- slot. See 'pushOptimized' and 'pushOptimized'' for use cases.
+pushNN :: Array Integer Ident
+pushNN = listArray (1,255) $ map (TxtI . mkFastString . ("h$pp"++) . show) [(1::Int)..255]
+
+-- | Like 'pushN'' but for the partial push functions
+pushNN' :: Array Integer JExpr
+pushNN' = fmap (ValExpr . JVar) pushNN
+
+pushOptimized' :: [(Id,Int)] -> G JStat
+pushOptimized' xs = do
+ slots <- getSlots
+ pushOptimized =<< (zipWithM f xs (slots++repeat SlotUnknown))
+ where
+ f (i1,n1) xs2 = do
+ xs <- varsForId i1
+ let !id_n1 = xs !! (n1-1)
+
+ case xs2 of
+ SlotId i2 n2 -> pure (id_n1,i1==i2&&n1==n2)
+ _ -> pure (id_n1,False)
+
+-- | optimized push that reuses existing values on stack automatically chooses
+-- an optimized partial push (h$ppN) function when possible.
+pushOptimized :: [(JExpr,Bool)] -- ^ contents of the slots, True if same value is already there
+ -> G JStat
+pushOptimized [] = return mempty
+pushOptimized xs = do
+ dropSlots l
+ modifyStackDepth (+ length xs)
+ go . csInlinePush <$> getSettings
+ where
+ go True = inlinePush
+ go _
+ | all snd xs = adjSp' l
+ | all (not.snd) xs && l <= 32 =
+ ApplStat (pushN' ! l) (map fst xs)
+ | l <= 8 && not (snd $ last xs) =
+ ApplStat (pushNN' ! sig) [ e | (e,False) <- xs ]
+ | otherwise = inlinePush
+ l = length xs
+ sig :: Integer
+ sig = L.foldl1' (Bits..|.) $ zipWith (\(_e,b) i -> if not b then Bits.bit i else 0) xs [0..]
+ inlinePush = adjSp' l <> mconcat (zipWith pushSlot [1..] xs)
+ pushSlot i (ex, False) = IdxExpr stack (offset i) |= ex
+ pushSlot _ _ = mempty
+ offset i | i == l = sp
+ | otherwise = InfixExpr SubOp sp (toJExpr (l - i))
+
+-- | push a let-no-escape frame onto the stack
+pushLneFrame :: HasDebugCallStack => Int -> ExprCtx -> G JStat
+pushLneFrame size ctx =
+ let ctx' = ctxLneShrinkStack ctx size
+ in pushOptimized' (ctxLneFrameVars ctx')
+
+-- | Pop things, don't update the stack knowledge in 'G'
+popSkip :: Int -- ^ number of slots to skip
+ -> [JExpr] -- ^ assign stack slot values to these
+ -> JStat
+popSkip 0 [] = mempty
+popSkip n [] = adjSpN' n
+popSkip n tgt = loadSkip n tgt <> adjSpN' (length tgt + n)
+
+-- | Load 'length (xs :: [JExpr])' things from the stack at offset 'n :: Int'.
+-- This function does no stack pointer manipulation, it merely indexes into the
+-- stack and loads payloads into 'xs'.
+loadSkip :: Int -> [JExpr] -> JStat
+loadSkip = loadSkipFrom sp
+ where
+ loadSkipFrom :: JExpr -> Int -> [JExpr] -> JStat
+ loadSkipFrom fr n xs = mconcat items
+ where
+ items = reverse $ zipWith f [(0::Int)..] (reverse xs)
+ -- helper to generate sp - n offset to index with
+ offset 0 = fr
+ offset n = InfixExpr SubOp fr (toJExpr n)
+ -- helper to load stack .! i into ex, e.g., ex = stack[i]
+ f i ex = ex |= IdxExpr stack (toJExpr (offset (i+n)))
+
+
+-- | Pop but preserve the first N slots
+popSkipI :: Int -> [(Ident,StackSlot)] -> G JStat
+popSkipI 0 [] = pure mempty
+popSkipI n [] = popN n
+popSkipI n xs = do
+ -- add N unknown slots
+ addUnknownSlots n
+ -- now add the slots from xs, after this line the stack should look like
+ -- [xs] ++ [Unknown...] ++ old_stack
+ addSlots (map snd xs)
+ -- move stack pointer into the stack by (length xs + n), basically resetting
+ -- the stack pointer
+ a <- adjSpN (length xs + n)
+ -- now load skipping first N slots
+ return (loadSkipI n (map fst xs) <> a)
+
+-- | Just like 'loadSkip' but operate on 'Ident's rather than 'JExpr'
+loadSkipI :: Int -> [Ident] -> JStat
+loadSkipI = loadSkipIFrom sp
+ where loadSkipIFrom :: JExpr -> Int -> [Ident] -> JStat
+ loadSkipIFrom fr n xs = mconcat items
+ where
+ items = reverse $ zipWith f [(0::Int)..] (reverse xs)
+ offset 0 = fr
+ offset n = InfixExpr SubOp fr (toJExpr n)
+ f i ex = ex ||= IdxExpr stack (toJExpr (offset (i+n)))
+
+-- | Blindly pop N slots
+popN :: Int -> G JStat
+popN n = addUnknownSlots n >> adjSpN n
+
+-- | Generate statements to update the current node with a blackhole
+bhStats :: StgToJSConfig -> Bool -> JStat
+bhStats s pushUpd = mconcat
+ [ if pushUpd then push' s [r1, var "h$upd_frame"] else mempty
+ , toJExpr R1 .^ closureEntry_ |= var "h$blackhole"
+ , toJExpr R1 .^ closureField1_ |= var "h$currentThread"
+ , toJExpr R1 .^ closureField2_ |= null_ -- will be filled with waiters array
+ ]
+
+-- | Wrapper around 'updateThunk'', performs the stack manipulation before
+-- updating the Thunk.
+updateThunk :: G JStat
+updateThunk = do
+ settings <- getSettings
+ -- update frame size
+ let adjPushStack :: Int -> G ()
+ adjPushStack n = do modifyStackDepth (+n)
+ dropSlots n
+ adjPushStack 2
+ return $ (updateThunk' settings)
+
+-- | Update a thunk by checking 'StgToJSConfig'. If the config inlines black
+-- holes then update inline, else make an explicit call to the black hole
+-- handler.
+updateThunk' :: StgToJSConfig -> JStat
+updateThunk' settings =
+ if csInlineBlackhole settings
+ then bhStats settings True
+ else ApplStat (var "h$bh") []
diff --git a/compiler/GHC/StgToJS/StaticPtr.hs b/compiler/GHC/StgToJS/StaticPtr.hs
new file mode 100644
index 0000000000..bddae1e674
--- /dev/null
+++ b/compiler/GHC/StgToJS/StaticPtr.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.StaticPtr
+ ( initStaticPtrs
+ )
+where
+
+import GHC.Prelude
+import GHC.Linker.Types (SptEntry(..))
+import GHC.Fingerprint.Type
+import GHC.Types.Literal
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.StgToJS.Types
+import GHC.StgToJS.Literal
+import GHC.StgToJS.Ids
+
+initStaticPtrs :: [SptEntry] -> G JStat
+initStaticPtrs ptrs = mconcat <$> mapM initStatic ptrs
+ where
+ initStatic (SptEntry sp_id (Fingerprint w1 w2)) = do
+ i <- varForId sp_id
+ fpa <- concat <$> mapM (genLit . mkLitWord64 . fromIntegral) [w1,w2]
+ let sptInsert = ApplExpr (var "h$hs_spt_insert") (fpa ++ [i])
+ return $ (var "h$initStatic" .^ "push") `ApplStat` [jLam sptInsert]
+
diff --git a/compiler/GHC/StgToJS/StgUtils.hs b/compiler/GHC/StgToJS/StgUtils.hs
new file mode 100644
index 0000000000..62c494c3a7
--- /dev/null
+++ b/compiler/GHC/StgToJS/StgUtils.hs
@@ -0,0 +1,266 @@
+{-# LANGUAGE LambdaCase #-}
+
+module GHC.StgToJS.StgUtils
+ ( bindingRefs
+ , hasExport
+ , collectTopIds
+ , collectIds
+ , removeTick
+ , isUpdatableRhs
+ , isInlineExpr
+ , exprRefs
+ -- * Live vars
+ , LiveVars
+ , liveVars
+ , liveStatic
+ , stgRhsLive
+ , stgExprLive
+ , stgTopBindLive
+ , stgLetNoEscapeLive
+ , stgLneLiveExpr
+ , stgLneLive
+ , stgLneLive'
+ )
+where
+
+import GHC.Prelude
+
+import GHC.Stg.Syntax
+import GHC.Core.DataCon
+import GHC.Core.Type
+import GHC.Core.TyCon
+
+import GHC.Types.Unique.FM
+import GHC.Types.Unique.Set
+import GHC.Types.Unique
+import GHC.Types.Id
+import GHC.Types.Id.Info
+import GHC.Types.ForeignCall
+import GHC.Types.TyThing
+import GHC.Types.Name
+import GHC.Types.Var.Set
+
+import GHC.Builtin.Names
+import GHC.Builtin.PrimOps (PrimOp(SeqOp), primOpIsReallyInline)
+import GHC.Utils.Misc (seqList)
+import GHC.Utils.Panic
+
+import qualified Data.Foldable as F
+import qualified Data.Set as S
+import qualified Data.List as L
+import Data.Set (Set)
+import Data.Monoid
+
+s :: a -> Set a
+s = S.singleton
+
+l :: (a -> Set Id) -> [a] -> Set Id
+l = F.foldMap
+
+-- | collect Ids that this binding refers to
+-- (does not include the bindees themselves)
+-- first argument is Id -> StgExpr map for unfloated arguments
+bindingRefs :: UniqFM Id CgStgExpr -> CgStgBinding -> Set Id
+bindingRefs u = \case
+ StgNonRec _ rhs -> rhsRefs u rhs
+ StgRec bs -> l (rhsRefs u . snd) bs
+
+rhsRefs :: UniqFM Id CgStgExpr -> CgStgRhs -> Set Id
+rhsRefs u = \case
+ StgRhsClosure _ _ _ _ body -> exprRefs u body
+ StgRhsCon _ccs d _mu _ticks args -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args
+
+exprRefs :: UniqFM Id CgStgExpr -> CgStgExpr -> Set Id
+exprRefs u = \case
+ StgApp f args -> s f <> l (argRefs u) args
+ StgConApp d _n args _ -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args
+ StgOpApp _ args _ -> l (argRefs u) args
+ StgLit {} -> mempty
+ StgCase expr _ _ alts -> exprRefs u expr <> mconcat (fmap (altRefs u) alts)
+ StgLet _ bnd expr -> bindingRefs u bnd <> exprRefs u expr
+ StgLetNoEscape _ bnd expr -> bindingRefs u bnd <> exprRefs u expr
+ StgTick _ expr -> exprRefs u expr
+
+altRefs :: UniqFM Id CgStgExpr -> CgStgAlt -> Set Id
+altRefs u alt = exprRefs u (alt_rhs alt)
+
+argRefs :: UniqFM Id CgStgExpr -> StgArg -> Set Id
+argRefs u = \case
+ StgVarArg id
+ | Just e <- lookupUFM u id -> exprRefs u e
+ | otherwise -> s id
+ _ -> mempty
+
+hasExport :: CgStgBinding -> Bool
+hasExport bnd =
+ case bnd of
+ StgNonRec b e -> isExportedBind b e
+ StgRec bs -> any (uncurry isExportedBind) bs
+ where
+ isExportedBind _i (StgRhsCon _cc con _ _ _) =
+ getUnique con == staticPtrDataConKey
+ isExportedBind _ _ = False
+
+collectTopIds :: CgStgBinding -> [Id]
+collectTopIds (StgNonRec b _) = [b]
+collectTopIds (StgRec bs) = let xs = map (zapFragileIdInfo . fst) bs
+ in seqList xs `seq` xs
+
+collectIds :: UniqFM Id CgStgExpr -> CgStgBinding -> [Id]
+collectIds unfloated b =
+ let xs = map zapFragileIdInfo .
+ filter acceptId $ S.toList (bindingRefs unfloated b)
+ in seqList xs `seq` xs
+ where
+ acceptId i = all ($ i) [not . isForbidden] -- fixme test this: [isExported[isGlobalId, not.isForbidden]
+ -- the GHC.Prim module has no js source file
+ isForbidden i
+ | Just m <- nameModule_maybe (getName i) = m == gHC_PRIM
+ | otherwise = False
+
+removeTick :: CgStgExpr -> CgStgExpr
+removeTick (StgTick _ e) = e
+removeTick e = e
+
+-----------------------------------------------------
+-- Live vars
+--
+-- TODO: should probably be moved into GHC.Stg.LiveVars
+
+type LiveVars = DVarSet
+
+liveStatic :: LiveVars -> LiveVars
+liveStatic = filterDVarSet isGlobalId
+
+liveVars :: LiveVars -> LiveVars
+liveVars = filterDVarSet (not . isGlobalId)
+
+stgTopBindLive :: CgStgTopBinding -> [(Id, LiveVars)]
+stgTopBindLive = \case
+ StgTopLifted b -> stgBindLive b
+ StgTopStringLit {} -> []
+
+stgBindLive :: CgStgBinding -> [(Id, LiveVars)]
+stgBindLive = \case
+ StgNonRec b rhs -> [(b, stgRhsLive rhs)]
+ StgRec bs -> map (\(b,rhs) -> (b, stgRhsLive rhs)) bs
+
+stgBindRhsLive :: CgStgBinding -> LiveVars
+stgBindRhsLive b =
+ let (bs, ls) = unzip (stgBindLive b)
+ in delDVarSetList (unionDVarSets ls) bs
+
+stgRhsLive :: CgStgRhs -> LiveVars
+stgRhsLive = \case
+ StgRhsClosure _ _ _ args e -> delDVarSetList (stgExprLive True e) args
+ StgRhsCon _ _ _ _ args -> unionDVarSets (map stgArgLive args)
+
+stgArgLive :: StgArg -> LiveVars
+stgArgLive = \case
+ StgVarArg occ -> unitDVarSet occ
+ StgLitArg {} -> emptyDVarSet
+
+stgExprLive :: Bool -> CgStgExpr -> LiveVars
+stgExprLive includeLHS = \case
+ StgApp occ args -> unionDVarSets (unitDVarSet occ : map stgArgLive args)
+ StgLit {} -> emptyDVarSet
+ StgConApp _dc _n args _tys -> unionDVarSets (map stgArgLive args)
+ StgOpApp _op args _ty -> unionDVarSets (map stgArgLive args)
+ StgCase e b _at alts
+ | includeLHS -> el `unionDVarSet` delDVarSet al b
+ | otherwise -> delDVarSet al b
+ where
+ al = unionDVarSets (map stgAltLive alts)
+ el = stgExprLive True e
+ StgLet _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b)
+ StgLetNoEscape _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b)
+ StgTick _ti e -> stgExprLive True e
+
+stgAltLive :: CgStgAlt -> LiveVars
+stgAltLive alt =
+ delDVarSetList (stgExprLive True (alt_rhs alt)) (alt_bndrs alt)
+
+stgLetNoEscapeLive :: Bool -> StgBinding -> StgExpr -> LiveVars
+stgLetNoEscapeLive _someBool _b _e = panic "stgLetNoEscapeLive"
+
+bindees :: CgStgBinding -> [Id]
+bindees = \case
+ StgNonRec b _e -> [b]
+ StgRec bs -> map fst bs
+
+isUpdatableRhs :: CgStgRhs -> Bool
+isUpdatableRhs (StgRhsClosure _ _ u _ _) = isUpdatable u
+isUpdatableRhs _ = False
+
+stgLneLive' :: CgStgBinding -> [Id]
+stgLneLive' b = filter (`notElem` bindees b) (stgLneLive b)
+
+stgLneLive :: CgStgBinding -> [Id]
+stgLneLive (StgNonRec _b e) = stgLneLiveExpr e
+stgLneLive (StgRec bs) = L.nub $ concatMap (stgLneLiveExpr . snd) bs
+
+stgLneLiveExpr :: CgStgRhs -> [Id]
+stgLneLiveExpr rhs = dVarSetElems (liveVars $ stgRhsLive rhs)
+-- stgLneLiveExpr (StgRhsClosure _ _ _ _ e) = dVarSetElems (liveVars (stgExprLive e))
+-- stgLneLiveExpr StgRhsCon {} = []
+
+-- | returns True if the expression is definitely inline
+isInlineExpr :: UniqSet Id -> CgStgExpr -> (UniqSet Id, Bool)
+isInlineExpr v = \case
+ StgApp i args
+ -> (emptyUniqSet, isInlineApp v i args)
+ StgLit{}
+ -> (emptyUniqSet, True)
+ StgConApp{}
+ -> (emptyUniqSet, True)
+ StgOpApp (StgFCallOp f _) _ _
+ -> (emptyUniqSet, isInlineForeignCall f)
+ StgOpApp (StgPrimOp SeqOp) [StgVarArg e] t
+ -> (emptyUniqSet, e `elementOfUniqSet` v || isStrictType t)
+ StgOpApp (StgPrimOp op) _ _
+ -> (emptyUniqSet, primOpIsReallyInline op)
+ StgOpApp (StgPrimCallOp _c) _ _
+ -> (emptyUniqSet, True)
+ StgCase e b _ alts
+ ->let (_ve, ie) = isInlineExpr v e
+ v' = addOneToUniqSet v b
+ (vas, ias) = unzip $ map (isInlineExpr v') (fmap alt_rhs alts)
+ vr = L.foldl1' intersectUniqSets vas
+ in (vr, (ie || b `elementOfUniqSet` v) && and ias)
+ StgLet _ b e
+ -> isInlineExpr (inspectInlineBinding v b) e
+ StgLetNoEscape _ _b e
+ -> isInlineExpr v e
+ StgTick _ e
+ -> isInlineExpr v e
+
+inspectInlineBinding :: UniqSet Id -> CgStgBinding -> UniqSet Id
+inspectInlineBinding v = \case
+ StgNonRec i r -> inspectInlineRhs v i r
+ StgRec bs -> foldl' (\v' (i,r) -> inspectInlineRhs v' i r) v bs
+
+inspectInlineRhs :: UniqSet Id -> Id -> CgStgRhs -> UniqSet Id
+inspectInlineRhs v i = \case
+ StgRhsCon{} -> addOneToUniqSet v i
+ StgRhsClosure _ _ ReEntrant _ _ -> addOneToUniqSet v i
+ _ -> v
+
+isInlineForeignCall :: ForeignCall -> Bool
+isInlineForeignCall (CCall (CCallSpec _ cconv safety)) =
+ not (playInterruptible safety) &&
+ not (cconv /= JavaScriptCallConv && playSafe safety)
+
+isInlineApp :: UniqSet Id -> Id -> [StgArg] -> Bool
+isInlineApp v i = \case
+ _ | isJoinId i -> False
+ [] -> isUnboxedTupleType (idType i) ||
+ isStrictType (idType i) ||
+ i `elementOfUniqSet` v
+
+ [StgVarArg a]
+ | DataConWrapId dc <- idDetails i
+ , isNewTyCon (dataConTyCon dc)
+ , isStrictType (idType a) || a `elementOfUniqSet` v || isStrictId a
+ -> True
+ _ -> False
+
diff --git a/compiler/GHC/StgToJS/Symbols.hs b/compiler/GHC/StgToJS/Symbols.hs
new file mode 100644
index 0000000000..999c654fa8
--- /dev/null
+++ b/compiler/GHC/StgToJS/Symbols.hs
@@ -0,0 +1,89 @@
+
+-- | JS symbol generation
+module GHC.StgToJS.Symbols
+ ( moduleGlobalSymbol
+ , moduleExportsSymbol
+ , mkJsSymbol
+ , mkJsSymbolBS
+ , mkFreshJsSymbol
+ , mkRawSymbol
+ , intBS
+ ) where
+
+import GHC.Prelude
+
+import GHC.Data.FastString
+import GHC.Unit.Module
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as BSC
+import qualified Data.ByteString.Builder as BSB
+import qualified Data.ByteString.Lazy as BSL
+
+-- | Hexadecimal representation of an int
+--
+-- Used for uniques. We could use base-62 as GHC usually does but this is likely
+-- faster.
+intBS :: Int -> ByteString
+intBS = BSL.toStrict . BSB.toLazyByteString . BSB.wordHex . fromIntegral
+
+-- | Return z-encoded unit:module
+unitModuleStringZ :: Module -> ByteString
+unitModuleStringZ mod = mconcat
+ [ fastZStringToByteString (zEncodeFS (unitIdFS (moduleUnitId mod)))
+ , BSC.pack "ZC" -- z-encoding for ":"
+ , fastZStringToByteString (zEncodeFS (moduleNameFS (moduleName mod)))
+ ]
+
+-- | the global linkable unit of a module exports this symbol, depend on it to
+-- include that unit (used for cost centres)
+moduleGlobalSymbol :: Module -> FastString
+moduleGlobalSymbol m = mkFastStringByteString $ mconcat
+ [ hd
+ , unitModuleStringZ m
+ , BSC.pack "_<global>"
+ ]
+
+moduleExportsSymbol :: Module -> FastString
+moduleExportsSymbol m = mkFastStringByteString $ mconcat
+ [ hd
+ , unitModuleStringZ m
+ , BSC.pack "_<exports>"
+ ]
+
+-- | Make JS symbol corresponding to the given Haskell symbol in the given
+-- module
+mkJsSymbolBS :: Bool -> Module -> FastString -> ByteString
+mkJsSymbolBS exported mod s = mconcat
+ [ if exported then hd else hdd
+ , unitModuleStringZ mod
+ , BSC.pack "zi" -- z-encoding of "."
+ , fastZStringToByteString (zEncodeFS s)
+ ]
+
+-- | Make JS symbol corresponding to the given Haskell symbol in the given
+-- module
+mkJsSymbol :: Bool -> Module -> FastString -> FastString
+mkJsSymbol exported mod s = mkFastStringByteString (mkJsSymbolBS exported mod s)
+
+-- | Make JS symbol for given module and unique.
+mkFreshJsSymbol :: Module -> Int -> FastString
+mkFreshJsSymbol mod i = mkFastStringByteString $ mconcat
+ [ hdd
+ , unitModuleStringZ mod
+ , BSC.pack "_"
+ , intBS i
+ ]
+
+-- | Make symbol "h$XYZ" or "h$$XYZ"
+mkRawSymbol :: Bool -> FastString -> FastString
+mkRawSymbol exported fs
+ | exported = mkFastStringByteString $ mconcat [ hd, bytesFS fs ]
+ | otherwise = mkFastStringByteString $ mconcat [ hdd, bytesFS fs ]
+
+-- | "h$$" constant string
+hdd :: ByteString
+hdd = BSC.pack "h$$"
+
+-- | "h$" constant string
+hd :: ByteString
+hd = BSC.take 2 hdd
diff --git a/compiler/GHC/StgToJS/Types.hs b/compiler/GHC/StgToJS/Types.hs
new file mode 100644
index 0000000000..2c01a30bf2
--- /dev/null
+++ b/compiler/GHC/StgToJS/Types.hs
@@ -0,0 +1,430 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE LambdaCase #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.StgToJS.Types
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file LICENSE)
+--
+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>
+-- Luite Stegeman <luite.stegeman@iohk.io>
+-- Sylvain Henry <sylvain.henry@iohk.io>
+-- Josh Meredith <josh.meredith@iohk.io>
+-- Stability : experimental
+--
+--
+-- Module that holds the Types required for the StgToJS pass
+-----------------------------------------------------------------------------
+
+module GHC.StgToJS.Types where
+
+import GHC.Prelude
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+import GHC.JS.Ppr ()
+
+import GHC.Stg.Syntax
+import GHC.Core.TyCon
+
+import GHC.Types.Unique
+import GHC.Types.Unique.FM
+import GHC.Types.Var
+import GHC.Types.ForeignCall
+
+import Control.Monad.Trans.State.Strict
+import GHC.Utils.Outputable (Outputable (..), text, SDocContext, (<+>), ($$))
+
+import GHC.Data.FastString
+import GHC.Data.FastMutInt
+
+import GHC.Unit.Module
+
+import qualified Data.Map as M
+import Data.Set (Set)
+import qualified Data.ByteString as BS
+import Data.Monoid
+import Data.Typeable (Typeable)
+import GHC.Generics (Generic)
+import Control.DeepSeq
+
+-- | A State monad over IO holding the generator state.
+type G = StateT GenState IO
+
+-- | The JS code generator state
+data GenState = GenState
+ { gsSettings :: !StgToJSConfig -- ^ codegen settings, read-only
+ , gsModule :: !Module -- ^ current module
+ , gsId :: {-# UNPACK #-} !FastMutInt -- ^ unique number for the id generator
+ , gsIdents :: !IdCache -- ^ hash consing for identifiers from a Unique
+ , gsUnfloated :: !(UniqFM Id CgStgExpr) -- ^ unfloated arguments
+ , gsGroup :: GenGroupState -- ^ state for the current binding group
+ , gsGlobal :: [JStat] -- ^ global (per module) statements (gets included when anything else from the module is used)
+ }
+
+-- | The JS code generator state relevant for the current binding group
+data GenGroupState = GenGroupState
+ { ggsToplevelStats :: [JStat] -- ^ extra toplevel statements for the binding group
+ , ggsClosureInfo :: [ClosureInfo] -- ^ closure metadata (info tables) for the binding group
+ , ggsStatic :: [StaticInfo] -- ^ static (CAF) data in our binding group
+ , ggsStack :: [StackSlot] -- ^ stack info for the current expression
+ , ggsStackDepth :: Int -- ^ current stack depth
+ , ggsExtraDeps :: Set OtherSymb -- ^ extra dependencies for the linkable unit that contains this group
+ , ggsGlobalIdCache :: GlobalIdCache
+ , ggsForeignRefs :: [ForeignJSRef]
+ }
+
+-- | The Configuration record for the StgToJS pass
+data StgToJSConfig = StgToJSConfig
+ -- flags
+ { csInlinePush :: !Bool
+ , csInlineBlackhole :: !Bool
+ , csInlineLoadRegs :: !Bool
+ , csInlineEnter :: !Bool
+ , csInlineAlloc :: !Bool
+ , csTraceRts :: !Bool
+ , csAssertRts :: !Bool
+ , csBoundsCheck :: !Bool
+ , csDebugAlloc :: !Bool
+ , csTraceForeign :: !Bool
+ , csProf :: !Bool -- ^ Profiling enabled
+ , csRuntimeAssert :: !Bool -- ^ Enable runtime assertions
+ -- settings
+ , csContext :: !SDocContext
+ }
+
+-- | Information relevenat to code generation for closures.
+data ClosureInfo = ClosureInfo
+ { ciVar :: Ident -- ^ object being infod
+ , ciRegs :: CIRegs -- ^ size of the payload (in number of JS values)
+ , ciName :: FastString -- ^ friendly name for printing
+ , ciLayout :: CILayout -- ^ heap/stack layout of the object
+ , ciType :: CIType -- ^ type of the object, with extra info where required
+ , ciStatic :: CIStatic -- ^ static references of this object
+ }
+ deriving stock (Eq, Show, Generic)
+
+-- | Closure information, 'ClosureInfo', registers
+data CIRegs
+ = CIRegsUnknown -- ^ A value witnessing a state of unknown registers
+ | CIRegs { ciRegsSkip :: Int -- ^ unused registers before actual args start
+ , ciRegsTypes :: [VarType] -- ^ args
+ }
+ deriving stock (Eq, Ord, Show, Generic)
+
+instance NFData CIRegs
+
+-- | Closure Information, 'ClosureInfo', layout
+data CILayout
+ = CILayoutVariable -- ^ layout stored in object itself, first position from the start
+ | CILayoutUnknown -- ^ fixed size, but content unknown (for example stack apply frame)
+ { layoutSize :: !Int
+ }
+ | CILayoutFixed -- ^ whole layout known
+ { layoutSize :: !Int -- ^ closure size in array positions, including entry
+ , layout :: [VarType] -- ^ The set of sized Types to layout
+ }
+ deriving stock (Eq, Ord, Show, Generic)
+
+instance NFData CILayout
+
+-- | The type of 'ClosureInfo'
+data CIType
+ = CIFun { citArity :: !Int -- ^ function arity
+ , citRegs :: !Int -- ^ number of registers for the args
+ }
+ | CIThunk -- ^ The closure is a THUNK
+ | CICon { citConstructor :: !Int } -- ^ The closure is a Constructor
+ | CIPap -- ^ The closure is a Partial Application
+ | CIBlackhole -- ^ The closure is a black hole
+ | CIStackFrame -- ^ The closure is a stack frame
+ deriving stock (Eq, Ord, Show, Generic)
+
+instance NFData CIType
+
+-- | Static references that must be kept alive
+newtype CIStatic = CIStaticRefs { staticRefs :: [FastString] }
+ deriving stock (Eq, Generic)
+ deriving newtype (Semigroup, Monoid, Show)
+
+-- | static refs: array = references, null = nothing to report
+-- note: only works after all top-level objects have been created
+instance ToJExpr CIStatic where
+ toJExpr (CIStaticRefs []) = null_ -- [je| null |]
+ toJExpr (CIStaticRefs rs) = toJExpr (map TxtI rs)
+
+-- | Free variable types
+data VarType
+ = PtrV -- ^ pointer = reference to heap object (closure object)
+ | VoidV -- ^ no fields
+ | DoubleV -- ^ A Double: one field
+ | IntV -- ^ An Int (32bit because JS): one field
+ | LongV -- ^ A Long: two fields one for the upper 32bits, one for the lower (NB: JS is little endian)
+ | AddrV -- ^ a pointer not to the heap: two fields, array + index
+ | RtsObjV -- ^ some RTS object from GHCJS (for example TVar#, MVar#, MutVar#, Weak#)
+ | ObjV -- ^ some JS object, user supplied, be careful around these, can be anything
+ | ArrV -- ^ boxed array
+ deriving stock (Eq, Ord, Enum, Bounded, Show, Generic)
+
+instance NFData VarType
+
+instance ToJExpr VarType where
+ toJExpr = toJExpr . fromEnum
+
+-- | The type of identifiers. These determine the suffix of generated functions
+-- in JS Land. For example, the entry function for the 'Just' constructor is a
+-- 'IdConEntry' which compiles to:
+-- @
+-- function h$baseZCGHCziMaybeziJust_con_e() { return h$rs() };
+-- @
+-- which just returns whatever the stack point is pointing to. Whereas the entry
+-- function to 'Just' is an 'IdEntry' and does the work. It compiles to:
+-- @
+-- function h$baseZCGHCziMaybeziJust_e() {
+-- var h$$baseZCGHCziMaybezieta_8KXnScrCjF5 = h$r2;
+-- h$r1 = h$c1(h$baseZCGHCziMaybeziJust_con_e, h$$baseZCGHCziMaybezieta_8KXnScrCjF5);
+-- return h$rs();
+-- };
+-- @
+-- Which loads some payload from register 2, and applies the Constructor Entry
+-- function for the Just to the payload, returns the result in register 1 and
+-- returns whatever is on top of the stack
+data IdType
+ = IdPlain -- ^ A plain identifier for values, no suffix added
+ | IdEntry -- ^ An entry function, suffix = "_e" in 'GHC.StgToJS.Ids.makeIdentForId'
+ | IdConEntry -- ^ A Constructor entry function, suffix = "_con_e" in 'GHC.StgToJS.Ids.makeIdentForId'
+ deriving (Enum, Eq, Ord)
+
+-- | Keys to differentiate Ident's in the ID Cache
+data IdKey
+ = IdKey !Int !Int !IdType
+ deriving (Eq, Ord)
+
+-- | Some other symbol
+data OtherSymb
+ = OtherSymb !Module !FastString
+ deriving Eq
+
+instance Ord OtherSymb where
+ compare (OtherSymb m1 t1) (OtherSymb m2 t2)
+ = stableModuleCmp m1 m2 <> lexicalCompareFS t1 t2
+
+-- | The identifier cache indexed on 'IdKey' local to a module
+newtype IdCache = IdCache (M.Map IdKey Ident)
+
+-- | The global Identifier Cache
+newtype GlobalIdCache = GlobalIdCache (UniqFM Ident (IdKey, Id))
+
+-- | A Stack Slot is either known or unknown. We avoid maybe here for more
+-- strictness.
+data StackSlot
+ = SlotId !Id !Int
+ | SlotUnknown
+ deriving (Eq, Ord)
+
+data StaticInfo = StaticInfo
+ { siVar :: !FastString -- ^ global object
+ , siVal :: !StaticVal -- ^ static initialization
+ , siCC :: !(Maybe Ident) -- ^ optional CCS name
+ } deriving stock (Eq, Show, Typeable, Generic)
+
+data StaticVal
+ = StaticFun !FastString [StaticArg]
+ -- ^ heap object for function
+ | StaticThunk !(Maybe (FastString,[StaticArg]))
+ -- ^ heap object for CAF (field is Nothing when thunk is initialized in an
+ -- alternative way, like string thunks through h$str)
+ | StaticUnboxed !StaticUnboxed
+ -- ^ unboxed constructor (Bool, Int, Double etc)
+ | StaticData !FastString [StaticArg]
+ -- ^ regular datacon app
+ | StaticList [StaticArg] (Maybe FastString)
+ -- ^ list initializer (with optional tail)
+ deriving stock (Eq, Show, Generic)
+
+data StaticUnboxed
+ = StaticUnboxedBool !Bool
+ | StaticUnboxedInt !Integer
+ | StaticUnboxedDouble !SaneDouble
+ | StaticUnboxedString !BS.ByteString
+ | StaticUnboxedStringOffset !BS.ByteString
+ deriving stock (Eq, Ord, Show, Generic)
+
+instance NFData StaticUnboxed
+
+-- | Static Arguments. Static Arguments are things that are statically
+-- allocated, i.e., they exist at program startup. These are static heap objects
+-- or literals or things that have been floated to the top level binding by ghc.
+data StaticArg
+ = StaticObjArg !FastString -- ^ reference to a heap object
+ | StaticLitArg !StaticLit -- ^ literal
+ | StaticConArg !FastString [StaticArg] -- ^ unfloated constructor
+ deriving stock (Eq, Show, Generic)
+
+instance Outputable StaticArg where
+ ppr x = text (show x)
+
+-- | A Static literal value
+data StaticLit
+ = BoolLit !Bool
+ | IntLit !Integer
+ | NullLit
+ | DoubleLit !SaneDouble -- should we actually use double here?
+ | StringLit !FastString
+ | BinLit !BS.ByteString
+ | LabelLit !Bool !FastString -- ^ is function pointer, label (also used for string / binary init)
+ deriving (Eq, Show, Generic)
+
+instance Outputable StaticLit where
+ ppr x = text (show x)
+
+
+instance ToJExpr StaticLit where
+ toJExpr (BoolLit b) = toJExpr b
+ toJExpr (IntLit i) = toJExpr i
+ toJExpr NullLit = null_
+ toJExpr (DoubleLit d) = toJExpr (unSaneDouble d)
+ toJExpr (StringLit t) = app (mkFastString "h$str") [toJExpr t]
+ toJExpr (BinLit b) = app (mkFastString "h$rstr") [toJExpr (map toInteger (BS.unpack b))]
+ toJExpr (LabelLit _isFun lbl) = var lbl
+
+-- | A foreign reference to some JS code
+data ForeignJSRef = ForeignJSRef
+ { foreignRefSrcSpan :: !FastString
+ , foreignRefPattern :: !FastString
+ , foreignRefSafety :: !Safety
+ , foreignRefCConv :: !CCallConv
+ , foreignRefArgs :: ![FastString]
+ , foreignRefResult :: !FastString
+ } deriving stock (Generic)
+
+-- | data used to generate one ObjUnit in our object file
+data LinkableUnit = LinkableUnit
+ { luObjUnit :: ObjUnit -- ^ serializable unit info
+ , luIdExports :: [Id] -- ^ exported names from haskell identifiers
+ , luOtherExports :: [FastString] -- ^ other exports
+ , luIdDeps :: [Id] -- ^ identifiers this unit depends on
+ , luPseudoIdDeps :: [Unique] -- ^ pseudo-id identifiers this unit depends on (fixme)
+ , luOtherDeps :: [OtherSymb] -- ^ symbols not from a haskell id that this unit depends on
+ , luRequired :: Bool -- ^ always link this unit
+ , luForeignRefs :: [ForeignJSRef]
+ }
+
+-- | one toplevel block in the object file
+data ObjUnit = ObjUnit
+ { oiSymbols :: ![FastString] -- ^ toplevel symbols (stored in index)
+ , oiClInfo :: ![ClosureInfo] -- ^ closure information of all closures in block
+ , oiStatic :: ![StaticInfo] -- ^ static closure data
+ , oiStat :: JStat -- ^ the code
+ , oiRaw :: !BS.ByteString -- ^ raw JS code
+ , oiFExports :: ![ExpFun]
+ , oiFImports :: ![ForeignJSRef]
+ }
+
+data ExpFun = ExpFun
+ { isIO :: !Bool
+ , args :: [JSFFIType]
+ , result :: !JSFFIType
+ } deriving (Eq, Ord, Show)
+
+-- | Types of FFI values
+data JSFFIType
+ = Int8Type
+ | Int16Type
+ | Int32Type
+ | Int64Type
+ | Word8Type
+ | Word16Type
+ | Word32Type
+ | Word64Type
+ | DoubleType
+ | ByteArrayType
+ | PtrType
+ | RefType
+ deriving (Show, Ord, Eq, Enum)
+
+
+-- | Typed expression
+data TypedExpr = TypedExpr
+ { typex_typ :: !PrimRep
+ , typex_expr :: [JExpr]
+ }
+
+instance Outputable TypedExpr where
+ ppr x = text "TypedExpr: " <+> ppr (typex_expr x)
+ $$ text "PrimReps: " <+> ppr (typex_typ x)
+
+-- | A Primop result is either an inlining of some JS payload, or a primitive
+-- call to a JS function defined in Shim files in base.
+data PrimRes
+ = PrimInline JStat -- ^ primop is inline, result is assigned directly
+ | PRPrimCall JStat -- ^ primop is async call, primop returns the next
+ -- function to run. result returned to stack top in registers
+
+data ExprResult
+ = ExprCont
+ | ExprInline (Maybe [JExpr])
+ deriving (Eq)
+
+newtype ExprValData = ExprValData [JExpr]
+ deriving newtype (Eq)
+
+-- | A Closure is one of six types
+data ClosureType
+ = Thunk -- ^ The closure is a THUNK
+ | Fun -- ^ The closure is a Function
+ | Pap -- ^ The closure is a Partial Application
+ | Con -- ^ The closure is a Constructor
+ | Blackhole -- ^ The closure is a Blackhole
+ | StackFrame -- ^ The closure is a stack frame
+ deriving (Show, Eq, Ord, Enum, Bounded)
+
+-- | Convert 'ClosureType' to an Int
+ctNum :: ClosureType -> Int
+ctNum Fun = 1
+ctNum Con = 2
+ctNum Thunk = 0
+ctNum Pap = 3
+ctNum Blackhole = 5
+ctNum StackFrame = -1
+
+-- | Convert 'ClosureType' to a String
+ctJsName :: ClosureType -> String
+ctJsName = \case
+ Thunk -> "CLOSURE_TYPE_THUNK"
+ Fun -> "CLOSURE_TYPE_FUN"
+ Pap -> "CLOSURE_TYPE_PAP"
+ Con -> "CLOSURE_TYPE_CON"
+ Blackhole -> "CLOSURE_TYPE_BLACKHOLE"
+ StackFrame -> "CLOSURE_TYPE_STACKFRAME"
+
+instance ToJExpr ClosureType where
+ toJExpr e = toJExpr (ctNum e)
+
+
+-- | A thread is in one of 4 states
+data ThreadStatus
+ = Running -- ^ The thread is running
+ | Blocked -- ^ The thread is blocked
+ | Finished -- ^ The thread is done
+ | Died -- ^ The thread has died
+ deriving (Show, Eq, Ord, Enum, Bounded)
+
+-- | Convert the status of a thread in JS land to an Int
+threadStatusNum :: ThreadStatus -> Int
+threadStatusNum = \case
+ Running -> 0
+ Blocked -> 1
+ Finished -> 16
+ Died -> 17
+
+-- | convert the status of a thread in JS land to a string
+threadStatusJsName :: ThreadStatus -> String
+threadStatusJsName = \case
+ Running -> "THREAD_RUNNING"
+ Blocked -> "THREAD_BLOCKED"
+ Finished -> "THREAD_FINISHED"
+ Died -> "THREAD_DIED"
diff --git a/compiler/GHC/StgToJS/Utils.hs b/compiler/GHC/StgToJS/Utils.hs
new file mode 100644
index 0000000000..8d16f39a64
--- /dev/null
+++ b/compiler/GHC/StgToJS/Utils.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module GHC.StgToJS.Utils
+ ( assignToTypedExprs
+ , assignCoerce1
+ , assignToExprCtx
+ )
+where
+
+import GHC.Prelude
+
+import GHC.StgToJS.Types
+import GHC.StgToJS.ExprCtx
+
+import GHC.JS.Syntax
+import GHC.JS.Make
+
+import GHC.Core.TyCon
+
+import GHC.Utils.Misc
+import GHC.Utils.Panic
+import GHC.Utils.Outputable
+
+assignToTypedExprs :: HasDebugCallStack => [TypedExpr] -> [JExpr] -> JStat
+assignToTypedExprs tes es =
+ assignAllEqual (concatMap typex_expr tes) es
+
+assignTypedExprs :: [TypedExpr] -> [TypedExpr] -> JStat
+assignTypedExprs tes es =
+ -- TODO: check primRep (typex_typ) here?
+ assignToTypedExprs tes (concatMap typex_expr es)
+
+assignToExprCtx :: HasDebugCallStack => ExprCtx -> [JExpr] -> JStat
+assignToExprCtx ctx es = assignToTypedExprs (ctxTarget ctx) es
+
+-- | Assign first expr only (if it exists), performing coercions between some
+-- PrimReps (e.g. StablePtr# and Addr#).
+assignCoerce1 :: HasDebugCallStack => [TypedExpr] -> [TypedExpr] -> JStat
+assignCoerce1 [x] [y] = assignCoerce x y
+assignCoerce1 [] [] = mempty
+assignCoerce1 x y = pprPanic "assignCoerce1"
+ (vcat [ text "lengths do not match"
+ , ppr x
+ , ppr y
+ ])
+
+-- | Assign p2 to p1 with optional coercion
+assignCoerce :: TypedExpr -> TypedExpr -> JStat
+-- Coercion between StablePtr# and Addr#
+assignCoerce (TypedExpr AddrRep [a_val, a_off]) (TypedExpr UnliftedRep [sptr]) = mconcat
+ [ a_val |= var "h$stablePtrBuf"
+ , a_off |= sptr
+ ]
+assignCoerce (TypedExpr UnliftedRep [sptr]) (TypedExpr AddrRep [_a_val, a_off]) =
+ sptr |= a_off
+assignCoerce p1 p2 = assignTypedExprs [p1] [p2]
+