PageRenderTime 67ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Gen2/Generator.hs

http://github.com/ghcjs/ghcjs
Haskell | 2380 lines | 1969 code | 202 blank | 209 comment | 141 complexity | 53ba0ea6b8d11c785bf0808556563920 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. {-# LANGUAGE CPP,
  2. QuasiQuotes,
  3. TupleSections,
  4. OverloadedStrings,
  5. LambdaCase,
  6. MultiWayIf,
  7. TemplateHaskell,
  8. ViewPatterns,
  9. BangPatterns
  10. #-}
  11. {-
  12. Main generator module
  13. -}
  14. module Gen2.Generator (generate) where
  15. import Fingerprint
  16. import ForeignCall
  17. import CostCentre
  18. import FastString
  19. import TysWiredIn
  20. import BasicTypes
  21. import ListSetOps
  22. import PrelNames
  23. import DynFlags
  24. import Encoding
  25. import UniqSet
  26. import Literal
  27. import DataCon
  28. import CoreSyn
  29. import IdInfo
  30. import TcType
  31. import UniqFM
  32. import Unique
  33. import StgSyn
  34. import PrimOp
  35. import Module
  36. import VarSet
  37. import Panic
  38. import TyCon
  39. import Util
  40. import Type hiding (typeSize)
  41. import RepType
  42. import TysPrim
  43. import Name
  44. import GHC
  45. import Id
  46. import HscTypes
  47. import Control.Applicative
  48. import Control.DeepSeq
  49. import Control.Lens hiding ((||=))
  50. import Control.Monad.State.Strict
  51. import Data.Array
  52. import Data.Bits
  53. ((.|.), shiftL, shiftR, (.&.), testBit, xor, complement)
  54. import Data.ByteString (ByteString)
  55. import qualified Data.ByteString as B
  56. import qualified Data.ByteString.Lazy as BL
  57. import Data.Char (ord, chr, isDigit)
  58. import Data.Either (partitionEithers)
  59. import Data.Function (on)
  60. import Data.Generics.Aliases (mkT)
  61. import Data.Generics.Schemes (everywhere)
  62. import Data.Int
  63. import Data.IntMap.Strict (IntMap)
  64. import qualified Data.IntMap.Strict as IM
  65. import qualified Data.IntSet as IS
  66. import Data.Maybe
  67. (isJust, isNothing, catMaybes, fromMaybe, maybeToList, listToMaybe)
  68. import Data.Map (Map)
  69. import qualified Data.Map as M
  70. import Data.Set (Set)
  71. import qualified Data.Set as S
  72. import Data.List
  73. (partition, intercalate, sort, sortBy, foldl', scanl')
  74. import qualified Data.List as L
  75. import qualified Data.Text as T
  76. import qualified Data.Text.Encoding as T
  77. import Data.Text (Text)
  78. import Compiler.JMacro
  79. import qualified Text.Parsec as P
  80. import Compiler.Compat
  81. import Compiler.Settings
  82. import Gen2.Base
  83. import Gen2.Deps
  84. import Gen2.Utils
  85. import Gen2.Prim
  86. import Gen2.Rts
  87. import Gen2.RtsTypes
  88. import Gen2.StgAst
  89. import Gen2.RtsAlloc
  90. import Gen2.RtsApply
  91. import qualified Gen2.Linker as Linker
  92. import Gen2.ClosureInfo
  93. import qualified Gen2.Optimizer as O
  94. import qualified Gen2.Object as Object
  95. import Gen2.Sinker
  96. import Gen2.Profiling
  97. import qualified Gen2.Compactor as Compactor
  98. import GHC.Float
  99. -- debug
  100. import Gen2.Printer (pretty)
  101. import qualified Data.Text.Lazy as TL
  102. import Text.PrettyPrint.Leijen.Text (displayT, renderPretty)
  103. import qualified Debug.Trace
  104. data DependencyDataCache = DDC
  105. { _ddcModule :: !(IntMap Object.Package) -- ^ Unique Module -> Object.Package
  106. , _ddcId :: !(IntMap Object.Fun) -- ^ Unique Id -> Object.Fun (only to other modules)
  107. , _ddcOther :: !(Map OtherSymb Object.Fun)
  108. }
  109. makeLenses ''DependencyDataCache
  110. type StgPgm = [StgBinding]
  111. data ExprCtx = ExprCtx
  112. { _ctxTop :: Id
  113. , _ctxTarget :: [(PrimRep,[JExpr])]
  114. , _ctxEval :: UniqSet Id
  115. , _ctxLne :: UniqSet Id -- ^ all lne-bound things
  116. , _ctxLneFrameBs :: UniqFM Int -- ^ binds in current lne frame (defined at size)
  117. , _ctxLneFrame :: [(Id,Int)] -- ^ contents of current lne frame
  118. , _ctxSrcSpan :: Maybe RealSrcSpan
  119. }
  120. makeLenses ''ExprCtx
  121. instance Show ExprCtx where
  122. show (ExprCtx top tgt eval lne _lnefbs lnef _mbSpan) =
  123. "ExprCtx\n" ++ unlines [show top, show tgt, sus eval, sus lne, show lnef]
  124. where
  125. sus = show . nonDetEltsUniqSet
  126. clearCtxStack :: ExprCtx -> ExprCtx
  127. clearCtxStack ctx = ctx & ctxLneFrameBs .~ emptyUFM
  128. & ctxLneFrame .~ []
  129. adjustCtxStack :: Int -> ExprCtx -> ExprCtx
  130. adjustCtxStack n ctx
  131. | l < n = panic $ "adjustCtxStack: let-no-escape stack too short: " ++
  132. show l ++ " < " ++ show n
  133. | otherwise = ctx & ctxLneFrame %~ take n
  134. where
  135. l = ctx ^. ctxLneFrame . to length
  136. addEval :: Id -> ExprCtx -> ExprCtx
  137. addEval i = over ctxEval (flip addOneToUniqSet i)
  138. generate :: GhcjsSettings
  139. -> DynFlags
  140. -> Module
  141. -> [StgTopBinding] -- StgPgm
  142. -> [SptEntry]
  143. -> CollectedCCs
  144. -> ByteString -- ^ binary data for the .js_o object file
  145. generate settings df m s spt_entries cccs =
  146. let (uf, s') = sinkPgm m s
  147. in trace' ("generate\n" ++ intercalate "\n\n" (map showIndent s)) $
  148. flip evalState (initState df m uf) $ do
  149. ifProfiling' $ initCostCentres cccs
  150. (st, lus) <- genUnits df m s' spt_entries
  151. -- (exported symbol names, javascript statements) for each linkable unit
  152. p <- forM lus $ \u ->
  153. mapM (fmap (\(TxtI i) -> i) . jsIdI) (luIdExports u) >>=
  154. \ts -> return (ts ++ luOtherExports u, luStat u)
  155. let (st', dbg) = dumpAst st settings df s'
  156. deps <- genDependencyData df m lus
  157. -- p first, so numbering of linkable units lines up
  158. pure . BL.toStrict $
  159. Object.object' st' deps (p ++ dbg)
  160. {- |
  161. Generate an extra linkable unit for the object file if -debug is active.
  162. this unit is never actually linked, but it contains the optimized STG AST
  163. so it can be easily reviewed using ghcjs --print-obj to aid in solving
  164. code generator problems.
  165. -}
  166. dumpAst :: Object.SymbolTable
  167. -> GhcjsSettings
  168. -> DynFlags
  169. -> [StgTopBinding]
  170. -> (Object.SymbolTable, [([Text], BL.ByteString)])
  171. dumpAst st _settings dflags s
  172. | buildingDebug dflags = (st', [(["h$debug", "h$dumpAst"], bs)])
  173. | otherwise = (st, [])
  174. where
  175. (st', bs) = Object.serializeStat st [] [] [j| h$dumpAst = `x` |] [] []
  176. x = T.intercalate "\n\n" (map (T.pack . showIndent) s)
  177. -- | variable prefix for the nth block in module
  178. modulePrefix :: Module -> Int -> Text
  179. modulePrefix m n =
  180. let encMod = zEncodeString . moduleNameString . moduleName $ m
  181. in T.pack $ "h$" ++ encMod ++ "_id_" ++ show n
  182. -- | data used to generate one ObjUnit in our object file
  183. data LinkableUnit = LinkableUnit
  184. { luStat :: BL.ByteString -- ^ serialized JS AST
  185. , luIdExports :: [Id] -- ^ exported names from haskell identifiers
  186. , luOtherExports :: [Text] -- ^ other exports
  187. , luIdDeps :: [Id] -- ^ identifiers this unit depends on
  188. , luOtherDeps :: [OtherSymb] -- ^ symbols not from a haskell id that this unit depends on
  189. , luRequired :: Bool -- ^ always link this unit
  190. , luForeignRefs :: [ForeignRef]
  191. } deriving (Eq, Ord, Show)
  192. -- | Generate the ingredients for the linkable units for this module
  193. genUnits :: HasDebugCallStack
  194. => DynFlags
  195. -> Module
  196. -> [StgTopBinding] -- StgPgm
  197. -> [SptEntry]
  198. -> G (Object.SymbolTable, [LinkableUnit]) -- ^ the final symbol table and the linkable units
  199. genUnits dflags m ss spt_entries = generateGlobalBlock =<< go 2 Object.emptySymbolTable ss
  200. where
  201. -- ss' = [l | StgTopLifted l <- ss]
  202. go :: HasDebugCallStack
  203. => Int -- ^ the block we're generating (block 0 is the global unit for the module)
  204. -> Object.SymbolTable -- ^ the shared symbol table
  205. -> [StgTopBinding]
  206. -> G (Object.SymbolTable, [LinkableUnit])
  207. go n st (x:xs) = do
  208. (st', mlu) <- generateBlock st x n
  209. (st'', lus) <- go (n+1) st' xs
  210. return (st'', maybe lus (:lus) mlu)
  211. go _ st [] = return (st, [])
  212. -- | Generate the global unit that all other blocks in the module depend on
  213. -- used for cost centres and static initializers
  214. -- the global unit has no dependencies, exports the moduleGlobalSymbol
  215. generateGlobalBlock :: HasDebugCallStack
  216. => (Object.SymbolTable, [LinkableUnit])
  217. -> G (Object.SymbolTable, [LinkableUnit])
  218. generateGlobalBlock (st, lus) = do
  219. glbl <- use gsGlobal
  220. staticInit <-
  221. initStaticPtrs spt_entries
  222. (st', _, bs) <- serializeLinkableUnit m st [] [] []
  223. ( O.optimize
  224. . jsSaturate (Just $ modulePrefix m 1)
  225. $ mconcat (reverse glbl) <> staticInit) [] []
  226. return ( st'
  227. , LinkableUnit bs
  228. []
  229. [moduleGlobalSymbol dflags m]
  230. []
  231. []
  232. False
  233. []
  234. : lus
  235. )
  236. -- | Generate the linkable unit for one binding or group of
  237. -- mutually recursive bindings
  238. generateBlock :: HasDebugCallStack
  239. => Object.SymbolTable
  240. -> StgTopBinding
  241. -> Int
  242. -> G (Object.SymbolTable, Maybe LinkableUnit)
  243. generateBlock st (StgTopStringLit bnd str) n = do
  244. bids <- genIdsI bnd
  245. case bids of
  246. [b1@(TxtI b1t),b2@(TxtI b2t)] -> do
  247. -- [e1,e2] <- genLit (MachStr str)
  248. emitStatic b1t (StaticUnboxed (StaticUnboxedString str)) Nothing
  249. emitStatic b2t (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing
  250. extraTl <- use (gsGroup . ggsToplevelStats)
  251. si <- use (gsGroup . ggsStatic)
  252. let stat = mempty -- mconcat (reverse extraTl) <> b1 ||= e1 <> b2 ||= e2
  253. (st', _ss, bs) <- serializeLinkableUnit m st [bnd] [] si
  254. (jsSaturate (Just $ modulePrefix m n) stat) [] []
  255. pure (st', Just $ LinkableUnit bs [bnd] [] [] [] False [])
  256. _ -> panic "generateBlock: invalid size"
  257. generateBlock st (StgTopLifted decl) n =
  258. trace' ("generateBlock:\n" ++ showIndent decl) $
  259. do
  260. tl <- genToplevel decl
  261. extraTl <- use (gsGroup . ggsToplevelStats)
  262. ci <- use (gsGroup . ggsClosureInfo)
  263. si <- use (gsGroup . ggsStatic)
  264. unf <- use gsUnfloated
  265. extraDeps <- use (gsGroup . ggsExtraDeps)
  266. fRefs <- use (gsGroup . ggsForeignRefs)
  267. resetGroup
  268. let allDeps = collectIds unf decl
  269. topDeps = collectTopIds decl
  270. required = hasExport decl
  271. stat = O.optimize
  272. . jsSaturate (Just $ modulePrefix m n)
  273. $ mconcat (reverse extraTl) <> tl
  274. (st', _ss, bs) <- serializeLinkableUnit m st topDeps ci si stat [] fRefs
  275. return $! seqList topDeps `seq` seqList allDeps `seq` st' `seq`
  276. (st', Just $ LinkableUnit bs topDeps [] allDeps (S.toList extraDeps) required fRefs)
  277. initStaticPtrs :: [SptEntry] -> C
  278. initStaticPtrs ptrs = mconcat <$> mapM initStatic ptrs
  279. where
  280. initStatic (SptEntry sp_id (Fingerprint w1 w2)) = do
  281. i <- jsId sp_id
  282. fpa <- concat <$> mapM (genLit . mkMachWord64 . fromIntegral) [w1,w2]
  283. let sptInsert = ApplExpr (ValExpr (JVar (TxtI "h$hs_spt_insert")))
  284. (fpa ++ [i])
  285. return [j| h$initStatic.push(function() {
  286. `sptInsert`;
  287. })
  288. |]
  289. hasExport :: StgBinding -> Bool
  290. hasExport bnd =
  291. case bnd of
  292. StgNonRec b e -> isExportedBind b e
  293. StgRec bs -> any (uncurry isExportedBind) bs
  294. where
  295. isExportedBind _i (StgRhsCon _cc con _) =
  296. getUnique con == staticPtrDataConKey
  297. isExportedBind _ _ = False
  298. {- |
  299. serialize the payload of a linkable unit in the object file, adding
  300. strings to the SymbolTable where necessary
  301. -}
  302. serializeLinkableUnit :: HasDebugCallStack
  303. => Module
  304. -> Object.SymbolTable -- symbol table to start with
  305. -> [Id] -- id's exported by unit
  306. -> [ClosureInfo]
  307. -> [StaticInfo]
  308. -> JStat -- generated code for the unit
  309. -> [Object.ExpFun]
  310. -> [Object.ForeignRef]
  311. -> G (Object.SymbolTable, [Text], BL.ByteString)
  312. serializeLinkableUnit _m st i ci si stat fe fi = do
  313. i' <- mapM idStr i
  314. let (st', o) = Object.serializeStat st ci si stat fe fi
  315. rnf i' `seq` rnf o `seq` return (st', i', o)
  316. where
  317. idStr i = itxt <$> jsIdI i
  318. collectTopIds :: StgBinding -> [Id]
  319. collectTopIds (StgNonRec b _) = [b]
  320. collectTopIds (StgRec bs) = let xs = map (zapFragileIdInfo . fst) bs
  321. in seqList xs `seq` xs
  322. collectIds :: UniqFM StgExpr -> StgBinding -> [Id]
  323. collectIds unfloated b =
  324. let xs = map zapFragileIdInfo .
  325. filter acceptId $ S.toList (bindingRefs unfloated b)
  326. in seqList xs `seq` xs
  327. where
  328. acceptId i = all ($ i) [not . isForbidden] -- fixme test this: [isExported[isGlobalId, not.isForbidden]
  329. -- the GHC.Prim module has no js source file
  330. isForbidden i
  331. | Just m <- nameModule_maybe (getName i) =
  332. moduleNameText m == T.pack "GHC.Prim" &&
  333. modulePackageKey m == primPackageKey
  334. | otherwise = False
  335. {- |
  336. generate the object's dependy data, taking care that package and module names
  337. are only stored once
  338. -}
  339. genDependencyData :: HasDebugCallStack
  340. => DynFlags
  341. -> Module
  342. -> [LinkableUnit]
  343. -> G Object.Deps
  344. genDependencyData dflags mod units = do
  345. -- [(blockindex, blockdeps, required, exported)]
  346. ds <- evalStateT (sequence (map (uncurry oneDep) blocks))
  347. (DDC IM.empty IM.empty M.empty)
  348. return $ Object.Deps (Linker.mkPackage $
  349. toInstalledUnitId (moduleUnitId mod))
  350. (moduleNameText mod)
  351. (IS.fromList [ n | (n, _, True, _) <- ds ])
  352. (M.fromList $ (\(n,_,_,es) -> map (,n) es) =<< ds)
  353. (listArray (0, length blocks-1) (ds ^.. traverse . _2))
  354. where
  355. -- Id -> Block
  356. unitIdExports :: UniqFM Int
  357. unitIdExports = listToUFM $
  358. concatMap (\(u,n) -> map (,n) (luIdExports u)) blocks
  359. -- OtherSymb -> Block
  360. unitOtherExports :: Map OtherSymb Int
  361. unitOtherExports = M.fromList $
  362. concatMap (\(u,n) -> map (,n)
  363. (map (OtherSymb mod)
  364. (luOtherExports u)))
  365. blocks
  366. blocks :: [(LinkableUnit, Int)]
  367. blocks = zip units [0..]
  368. -- generate the list of exports and set of dependencies for one unit
  369. oneDep :: LinkableUnit
  370. -> Int
  371. -> StateT DependencyDataCache G (Int, Object.BlockDeps, Bool, [Object.Fun])
  372. oneDep (LinkableUnit _ idExports otherExports idDeps otherDeps req frefs) n = do
  373. (edi, bdi) <- partitionEithers <$> mapM (lookupIdFun n) idDeps
  374. (edo, bdo) <- partitionEithers <$> mapM lookupOtherFun otherDeps
  375. expi <- mapM lookupExportedId (filter isExportedId idExports)
  376. expo <- mapM lookupExportedOther otherExports
  377. -- fixme thin deps, remove all transitive dependencies!
  378. let bdeps = Object.BlockDeps
  379. (IS.toList . IS.fromList . filter (/=n) $ bdi++bdo)
  380. (S.toList . S.fromList $ edi++edo)
  381. -- [] -- fixme support foreign exported
  382. -- frefs
  383. return (n, bdeps, req, expi++expo)
  384. idModule :: Id -> Maybe Module
  385. idModule i = nameModule_maybe (getName i) >>= \m ->
  386. guard (m /= mod) >> return m
  387. -- get the function for an Id from the cache, add it if necessary
  388. -- result: Left Object.Fun if function refers to another module
  389. -- Right blockNumber if function refers to current module
  390. --
  391. -- assumes function is internal to the current block if it's
  392. -- from teh current module and not in the unitIdExports map.
  393. lookupIdFun :: Int -> Id
  394. -> StateT DependencyDataCache G (Either Object.Fun Int)
  395. lookupIdFun n i = case lookupUFM unitIdExports i of
  396. Just k -> return (Right k)
  397. Nothing -> case idModule i of
  398. Nothing -> return (Right n)
  399. Just m ->
  400. let k = getKey . getUnique $ i
  401. addEntry :: StateT DependencyDataCache G Object.Fun
  402. addEntry = do
  403. (TxtI idTxt) <- lift (jsIdI i)
  404. lookupExternalFun (Just k) (OtherSymb m idTxt)
  405. in if m == mod
  406. then panic ("local id not found: " ++ show m)
  407. else Left <$> (maybe addEntry return =<<
  408. use (ddcId . to (IM.lookup k)))
  409. -- get the function for an OtherSymb from the cache, add it if necessary
  410. lookupOtherFun :: OtherSymb
  411. -> StateT DependencyDataCache G (Either Object.Fun Int)
  412. lookupOtherFun od@(OtherSymb m idTxt) =
  413. case M.lookup od unitOtherExports of
  414. Just n -> return (Right n)
  415. Nothing | m == mod -> panic ("genDependencyData.lookupOtherFun: unknown local other id: " ++ T.unpack idTxt)
  416. Nothing -> Left <$> (maybe (lookupExternalFun Nothing od) return =<<
  417. use (ddcOther . to (M.lookup od)))
  418. lookupExportedId :: Id -> StateT DependencyDataCache G Object.Fun
  419. lookupExportedId i = do
  420. (TxtI idTxt) <- lift (jsIdI i)
  421. lookupExternalFun (Just . getKey . getUnique $ i) (OtherSymb mod idTxt)
  422. lookupExportedOther :: Text -> StateT DependencyDataCache G Object.Fun
  423. lookupExportedOther = lookupExternalFun Nothing . OtherSymb mod
  424. -- lookup a dependency to another module, add to the id cache if there's
  425. -- an id key, otherwise add to other cache
  426. lookupExternalFun :: Maybe Int
  427. -> OtherSymb -> StateT DependencyDataCache G Object.Fun
  428. lookupExternalFun mbIdKey od@(OtherSymb m idTxt) = do
  429. let mk = getKey . getUnique $ m
  430. mpk = Linker.mkPackage (toInstalledUnitId (moduleUnitId m))
  431. inCache p = Object.Fun p (moduleNameText m) idTxt
  432. addCache = do
  433. let cache' = IM.insert mk mpk
  434. ddcModule %= cache'
  435. cache' `seq` return (Object.Fun mpk (moduleNameText m) idTxt)
  436. f <- maybe addCache (return . inCache) =<<
  437. use (ddcModule . to (IM.lookup mk))
  438. maybe (ddcOther %= M.insert od f) (\k -> ddcId %= IM.insert k f) mbIdKey
  439. return f
  440. moduleNameText :: Module -> Text
  441. moduleNameText m
  442. | xs == ":Main" = T.pack "Main"
  443. | otherwise = T.pack xs
  444. where xs = moduleNameString . moduleName $ m
  445. genToplevel :: StgBinding -> C
  446. genToplevel (StgNonRec bndr rhs) = genToplevelDecl bndr rhs
  447. genToplevel (StgRec bs) =
  448. mconcat $ map (\(bndr, rhs) -> genToplevelDecl bndr rhs) bs
  449. -- entry function of the worker
  450. enterDataCon :: DataCon -> G JExpr
  451. enterDataCon d = jsDcEntryId (dataConWorkId d)
  452. enterDataConI :: DataCon -> G Ident
  453. enterDataConI d = jsDcEntryIdI (dataConWorkId d)
  454. genToplevelDecl :: Id -> StgRhs -> C
  455. genToplevelDecl i rhs = do
  456. s1 <- resetSlots (genToplevelConEntry i rhs)
  457. s2 <- resetSlots (genToplevelRhs i rhs)
  458. return (s1 <> s2)
  459. genToplevelConEntry :: Id -> StgRhs -> C
  460. genToplevelConEntry i rhs@(StgRhsCon _cc con _args)
  461. | i `elem` [ i' | AnId i' <- dataConImplicitTyThings con ]
  462. = genSetConInfo i con (stgRhsLive rhs) -- NoSRT
  463. genToplevelConEntry i rhs@(StgRhsClosure _cc _bi [] _upd_flag
  464. _args (removeTick -> StgConApp dc _cargs _))
  465. | i `elem` [ i' | AnId i' <- dataConImplicitTyThings dc ]
  466. = genSetConInfo i dc (stgRhsLive rhs) -- srt
  467. genToplevelConEntry _ _ = mempty
  468. removeTick :: StgExpr -> StgExpr
  469. removeTick (StgTick _ e) = e
  470. removeTick e = e
  471. genStaticRefsRhs :: StgRhs -> G CIStatic
  472. genStaticRefsRhs lv = genStaticRefs (stgRhsLive lv)
  473. -- fixme, update to new way to compute static refs dynamically
  474. genStaticRefs :: LiveVars -> G CIStatic
  475. genStaticRefs lv
  476. | isEmptyDVarSet sv = return noStatic
  477. | otherwise = do
  478. unfloated <- use gsUnfloated
  479. let xs = filter (\x -> not (elemUFM x unfloated ||
  480. isLiftedType_maybe (idType x) == Just False))
  481. (dVarSetElems sv)
  482. CIStaticRefs . catMaybes <$> mapM getStaticRef xs
  483. where
  484. sv = liveStatic lv
  485. getStaticRef :: Id -> G (Maybe Text)
  486. getStaticRef = fmap (fmap itxt . listToMaybe) . genIdsI
  487. genToplevelRhs :: Id
  488. -> StgRhs
  489. -> C
  490. genToplevelRhs i rhs@(StgRhsClosure cc _bi _ upd args body)
  491. -- foreign exports
  492. | (StgOpApp (StgFCallOp (CCall (CCallSpec (StaticTarget _ t _ _) _ _)) _)
  493. [StgLitArg _ {- (MachInt _is_js_conv) -}, StgLitArg (MachStr _js_name), StgVarArg _tgt] _) <- body,
  494. t == fsLit "__mkExport" = return mempty -- fixme error "export not implemented"
  495. -- general cases:
  496. genToplevelRhs i (StgRhsCon cc con args) = do
  497. ii <- jsIdI i
  498. allocConStatic ii cc con args
  499. return mempty
  500. genToplevelRhs i rhs@(StgRhsClosure cc _bi [] _upd_flag {- srt -} args body) = do
  501. eid@(TxtI eidt) <- jsEnIdI i
  502. (TxtI idt) <- jsIdI i
  503. -- pushGlobalRefs
  504. body <- genBody (ExprCtx i [] emptyUniqSet emptyUniqSet emptyUFM [] Nothing) i R2 args body
  505. (lidents, lids) <- unzip <$> liftToGlobal (jsSaturate (Just . T.pack $ "ghcjs_tmp_sat_") body)
  506. let lidents' = map (\(TxtI t) -> t) lidents
  507. -- li
  508. -- refs <- popGlobalRefs
  509. CIStaticRefs sr0 <- genStaticRefsRhs rhs
  510. let sri = filter (`notElem` lidents') sr0
  511. sr = CIStaticRefs sri
  512. -- emitToplevel $ AssignStat (ValExpr (JVar $ TxtI ("h$globalRefs_" <> idt)))
  513. -- (ValExpr (JList $ map (ValExpr . JVar) lidents ++ [jnull] ++ map (ValExpr . JVar . TxtI) sri))
  514. et <- genEntryType args
  515. ll <- loadLiveFun lids
  516. (static, regs, upd) <-
  517. if et == CIThunk
  518. then (StaticThunk (Just (eidt, map StaticObjArg lidents')), CIRegs 0 [PtrV],) <$> updateThunk
  519. else return (StaticFun eidt (map StaticObjArg lidents'),
  520. (if null lidents then CIRegs 1 (concatMap idVt args)
  521. else CIRegs 0 (PtrV : concatMap idVt args))
  522. , mempty)
  523. setcc <- ifProfiling $
  524. if et == CIThunk
  525. then enterCostCentreThunk
  526. else enterCostCentreFun cc
  527. emitClosureInfo (ClosureInfo eidt
  528. regs
  529. idt
  530. (fixedLayout $ map (uTypeVt . idType) lids) -- (CILayoutFixed 0 [])
  531. et
  532. sr)
  533. ccId <- costCentreStackLbl cc
  534. emitStatic idt static ccId
  535. return $ eid ||= JFunc [] (ll <> upd <> setcc <> body)
  536. genToplevelRhs _ _ = panic "genToplevelRhs: top-level values cannot have live variables"
  537. dumpGlobalIdCache :: Text -> G ()
  538. dumpGlobalIdCache itxt = do
  539. GlobalIdCache gidc <- use globalIdCache
  540. let i = TxtI ("h$globalIdCache_" <> itxt)
  541. vs = M.keys
  542. emitToplevel [j| `i` = `M.keys gidc`; |]
  543. {- emitToplevel $ [j|
  544. AssignStat (ValExpr (JVar . TxtI $ "h$globalIdCache_" <> idt))
  545. (ValExpr (JList
  546. -}
  547. liftToGlobal :: JStat -> G [(Ident, Id)]
  548. liftToGlobal jst = do
  549. GlobalIdCache gidc <- use globalIdCache
  550. let sids = filter (`M.member` gidc) (jst ^.. Compactor.identsS)
  551. cnt = M.fromListWith (+) (map (,1) sids)
  552. sids' = sortBy (compare `on` (cnt M.!)) (nub' sids)
  553. pure $ map (\s -> (s, snd $ gidc M.! s)) sids'
  554. nub' :: (Ord a, Eq a) => [a] -> [a]
  555. nub' xs = go S.empty xs
  556. where
  557. go _ [] = []
  558. go s xxs@(x:xs) | S.member x s = go s xs
  559. | otherwise = x : go (S.insert x s) xs
  560. -- ids = filter M.member gidc
  561. {-
  562. algorithm:
  563. - collect all Id refs that are in the cache, count usage
  564. - order by increasing use
  565. - prepend loading lives var to body: body can stay the same
  566. -}
  567. {-
  568. todo for stack frames:
  569. - change calling convention?
  570. - return stack[sp] -> return stack[sp].f ?
  571. -> no we miss the continuation object then
  572. -> set h$rS
  573. -> return h$rs(); instead
  574. -}
  575. loadLiveFun :: [Id] -> C
  576. loadLiveFun l = do
  577. l' <- concat <$> mapM genIdsI l
  578. case l' of
  579. [] -> return mempty
  580. [v] -> return (decl' v [je| `R1`.d1 |])
  581. [v1,v2] -> return (decl' v1 [je| `R1`.d1 |] <> decl' v2 [je| `R1`.d2 |])
  582. (v:vs) -> do
  583. d <- makeIdent
  584. let l'' = mconcat . zipWith (loadLiveVar $ toJExpr d) [(1::Int)..] $ vs
  585. return (decl' v [je| `R1`.d1 |] <> [j| `decl d`; `d` = `R1`.d2 |] <> l'')
  586. where
  587. loadLiveVar d n v = let ident = dataFields ! n
  588. in decl' v (SelExpr d ident)
  589. dataFields :: Array Int Ident
  590. dataFields = listArray (1,16384) (map (TxtI . T.pack . ('d':) . show) [(1::Int)..16384])
  591. genBody :: HasDebugCallStack => ExprCtx -> Id -> StgReg -> [Id] -> StgExpr -> C
  592. genBody ctx i startReg args e =
  593. -- trace' ("genBody: " ++ show args)
  594. (genBody0 ctx i startReg args e)
  595. genBody0 :: HasDebugCallStack
  596. => ExprCtx
  597. -> Id
  598. -> StgReg
  599. -> [Id]
  600. -> StgExpr
  601. -> C
  602. genBody0 ctx i startReg args e = do
  603. la <- loadArgs startReg args
  604. lav <- verifyRuntimeReps args
  605. let ids :: [(PrimRep, [JExpr])]
  606. ids = -- take (resultSize args $ idType i) (map toJExpr $ enumFrom R1)
  607. reverse . fst $
  608. foldl' (\(rs, vs) (rep, size) ->
  609. let (vs0, vs1) = splitAt size vs
  610. in ((rep, vs0):rs,vs1))
  611. ([], map toJExpr $ enumFrom R1)
  612. (resultSize args $ idType i)
  613. (e, _r) <- trace' ("genBody0 ids:\n" ++ show ids) (genExpr (ctx & ctxTarget .~ ids) e)
  614. return $ la <> lav <> e <> returnStack -- [j| return `Stack`[`Sp`]; |]
  615. -- find the result type after applying the function to the arguments
  616. resultSize :: HasDebugCallStack => [Id] -> Type -> [(PrimRep, Int)]
  617. resultSize xs t = trace' ("resultSize\n" ++ show xs ++ "\n" ++ show t)
  618. (let r = resultSize0 xs t
  619. in trace' ("resultSize -> " ++ show r) r
  620. )
  621. resultSize0 :: HasDebugCallStack
  622. => [Id]
  623. -> Type
  624. -> [(PrimRep, Int)] -- Int
  625. resultSize0 xxs@(x:xs) t
  626. -- - | isUnboxedTupleType
  627. -- - | t' <- piResultTys t (map idType xxs) = resultSize0 [] t'
  628. -- - | MultiRep _ <- {- trace' "resultSize0 ubx" -} (repType (idType x)) = panic "genBody: unboxed tuple argument"
  629. -- - | otherwise = {- trace' "resultSize0 not" $ -}
  630. | t' <- unwrapType t
  631. , Just (fa, fr) <- splitFunTy_maybe t' -- isFunTy t' =
  632. , Just (tc, ys) <- splitTyConApp_maybe fa
  633. , isUnboxedTupleTyCon tc =
  634. resultSize0 xxs (mkFunTys (dropRuntimeRepArgs ys) fr)
  635. | t' <- unwrapType t
  636. , Just (fa, fr) <- splitFunTy_maybe t' = -- isFunTy t' =
  637. resultSize0 xs fr
  638. -- let (fa, fr) = splitFunTy t'
  639. -- let t'' = mkFunTys (map primRepToType . typePrimRep $ unwrapType fa) fr
  640. -- in resultSize0 xs (maybe fr snd . splitFunTy_maybe $ t'')
  641. | otherwise = [(LiftedRep, 1)] -- possibly newtype family, must be boxed
  642. -- case typePrimRep (unwrapType t) of -- repType t of
  643. -- (UnaryRep t' | isFunTy t' ->
  644. -- let (fa,fr) = splitFunTy t'
  645. -- t'' = mkFunTys (map slotTyToType . repTypeSlots $ repType fa) fr
  646. -- in {- trace' ("resultSize0 fun: " ++ show (fa, fr)) $ -}
  647. -- resultSize0 xs (snd . splitFunTy $ t'')
  648. -- _ -> 1 -- possibly newtype family, must be boxed
  649. resultSize0 [] t
  650. | isRuntimeRepKindedTy t' = []
  651. | isRuntimeRepTy t' = []
  652. | Nothing <- isLiftedType_maybe t' = [(LiftedRep, 1)]
  653. | otherwise = typeTarget t
  654. where
  655. t' = unwrapType t
  656. -- map (\t -> (t, varSize (primRepVt t))) $ typePrimRep (unwrapType t)
  657. {- trace' "resultSize0 eol" $ -}
  658. -- case repType t of
  659. -- UnaryRep t' -> {- trace' ("resultSize0 eol2: " ++ show t') $ -} typeSize t'
  660. -- MultiRep tys -> {- trace' ("resultSize0 eol3: " ++ show tys) $ -} sum (map (typeSize . slotTyToType) tys)
  661. loadArgs :: HasDebugCallStack => StgReg -> [Id] -> C
  662. loadArgs start args = do
  663. args' <- concatMapM genIdArgI args
  664. return (mconcat $ zipWith (||=) args' [start..])
  665. data ExprResult = ExprCont
  666. | ExprInline (Maybe [JExpr])
  667. deriving (Eq, Ord, Show)
  668. data ExprValData = ExprValData [JExpr]
  669. deriving (Eq, Ord, Show)
  670. -- not a Monoid
  671. branchResult :: HasDebugCallStack => [ExprResult] -> ExprResult
  672. branchResult [] = panic "branchResult: empty list"
  673. branchResult [e] = e
  674. branchResult (ExprCont:_) = ExprCont
  675. branchResult (_:es)
  676. | any (==ExprCont) es = ExprCont
  677. | otherwise = ExprInline Nothing
  678. genExpr :: HasDebugCallStack => ExprCtx -> StgExpr -> G (JStat, ExprResult)
  679. genExpr top e = trace' ("genExpr\n" ++ showIndent e)
  680. (genExpr0 top e)
  681. genExpr0 :: HasDebugCallStack
  682. => ExprCtx
  683. -> StgExpr
  684. -> G (JStat, ExprResult)
  685. genExpr0 top (StgApp f args) = genApp top f args
  686. genExpr0 top (StgLit l) =
  687. -- fixme check primRep here?
  688. (,ExprInline Nothing) .
  689. assignAllCh ("genExpr StgLit " ++ show (top ^. ctxTarget))
  690. (concatMap snd $ top ^. ctxTarget)
  691. <$> genLit l
  692. genExpr0 top (StgConApp con args _) = do
  693. as <- concatMapM genArg args
  694. c <- genCon top con as
  695. return (c, ExprInline (Just as))
  696. genExpr0 top (StgOpApp (StgFCallOp f _) args t) =
  697. genForeignCall top f t (concatMap snd $ top ^. ctxTarget) args
  698. genExpr0 top (StgOpApp (StgPrimOp op) args t) = genPrimOp top op args t
  699. genExpr0 top (StgOpApp (StgPrimCallOp c) args t) = genPrimCall top c args t
  700. genExpr0 _ (StgLam{}) = panic "genExpr: StgLam"
  701. genExpr0 top stg@(StgCase e b at alts) =
  702. genCase top b e at alts (liveVars $ stgExprLive False stg)
  703. genExpr0 top (StgLet b e) = do
  704. (b',top') <- genBind top b
  705. (s,r) <- genExpr top' e
  706. return (b' <> s, r)
  707. genExpr0 top (StgLetNoEscape b e) = do
  708. (b', top') <- genBindLne top b
  709. (s, r) <- genExpr top' e
  710. return (b' <> s, r)
  711. genExpr0 top (StgTick (ProfNote cc count scope) e) = do
  712. setSCCstats <- ifProfilingM $ setCC cc count scope
  713. (stats, result) <- genExpr top e
  714. return (setSCCstats <> stats, result)
  715. genExpr0 top (StgTick (SourceNote span _sname) e) =
  716. genExpr (top & ctxSrcSpan .~ Just span) e
  717. genExpr0 top (StgTick _m e) = genExpr top e
  718. might_be_a_function :: HasDebugCallStack => Type -> Bool
  719. -- Return False only if we are *sure* it's a data type
  720. -- Look through newtypes etc as much as poss
  721. might_be_a_function ty
  722. | [LiftedRep] <- typePrimRep ty
  723. , Just tc <- tyConAppTyCon_maybe (unwrapType ty)
  724. , isDataTyCon tc
  725. = False
  726. | otherwise
  727. = True
  728. matchVarName :: String -> FastString -> FastString -> Id -> Bool
  729. matchVarName pkg modu occ (idName -> n)
  730. | Just m <- nameModule_maybe n =
  731. occ == occNameFS (nameOccName n) &&
  732. modu == moduleNameFS (moduleName m) &&
  733. pkg `L.isPrefixOf` (unitIdString (moduleUnitId m))
  734. | otherwise = False
  735. genApp :: HasDebugCallStack
  736. => ExprCtx
  737. -> Id
  738. -> [StgArg]
  739. -> G (JStat, ExprResult)
  740. -- special cases for JSString literals
  741. -- we could handle unpackNBytes# here, but that's probably not common
  742. -- enough to warrant a special case
  743. genApp ctx i [StgVarArg v]
  744. | [top] <- concatMap snd (ctx ^. ctxTarget)
  745. -- , Just (Lit (MachStr bs)) <- expandUnfolding_maybe (idUnfolding v)
  746. -- , Just t <- decodeModifiedUTF8 bs -- unpackFS fs -- Just t <- decodeModifiedUTF8 bs
  747. , matchVarName "ghcjs-prim" "GHCJS.Prim" "unsafeUnpackJSStringUtf8##" i =
  748. (,ExprInline Nothing) . assignj top . ApplExpr (ValExpr (JVar (TxtI "h$decodeUtf8z")))
  749. <$> genIds v
  750. genApp ctx i [StgLitArg (MachStr bs), x]
  751. | [top] <- concatMap snd (ctx ^. ctxTarget), getUnique i == unpackCStringAppendIdKey, Just d <- decodeModifiedUTF8 bs = do
  752. -- fixme breaks assumption in codegen if bs doesn't decode
  753. prof <- csProf <$> use gsSettings
  754. let profArg = if prof then [jCafCCS] else []
  755. a <- genArg x
  756. return ([j| `top` = `ApplExpr (jsv "h$appendToHsStringA") $ [toJExpr d, toJExpr a] ++ profArg`; |]
  757. ,ExprInline Nothing)
  758. genApp top i a
  759. | Just n <- top ^. ctxLneFrameBs . to (flip lookupUFM i) = do -- let-no-escape
  760. as' <- concatMapM genArg a
  761. ei <- jsEntryId i
  762. let ra = mconcat . reverse $
  763. zipWith (\r a -> [j| `r` = `a`; |]) [R1 ..] as'
  764. p <- pushLneFrame n top
  765. a <- adjSp 1 -- for the header (which will only be written when the thread is suspended)
  766. return (ra <> p <> a <> [j| return `ei`; |], ExprCont)
  767. | n == 0 && (isUnboxedTupleType (idType i) || isStrictType (idType i)) = do
  768. a <- assignAllCh1 "genApp" (top ^. ctxTarget) .
  769. (alignTarget (idTarget i)) <$> genIds i
  770. return (a, ExprInline Nothing)
  771. | [vt] <- idVt i, isUnboxable vt && n == 0 && i `elementOfUniqSet` (top ^. ctxEval) = do
  772. let [c] = concatMap snd $ top ^. ctxTarget
  773. is <- genIds i
  774. case is of
  775. [i'] ->
  776. return ([j| `c` = (typeof `i'` === 'object') ? `i'`.d1 : `i'`; |]
  777. ,ExprInline Nothing)
  778. _ -> panic "genApp: invalid size"
  779. | n == 0 && (i `elementOfUniqSet` (top ^. ctxEval) || isStrictId i) = do
  780. a <- assignAllCh1 ("genApp:" ++ show i ++ " " ++ show (idFunRepArity i, idVt i))
  781. (top ^. ctxTarget) .
  782. (alignTarget (idTarget i))
  783. <$> genIds i
  784. settings <- use gsSettings
  785. let ww = case concatMap snd (top ^. ctxTarget) of
  786. [t] | csAssertRts settings ->
  787. [j| if(typeof `t` === 'object' && `isThunk t`)
  788. throw "unexpected thunk";
  789. |]
  790. _ -> mempty
  791. return (a <> ww, ExprInline Nothing)
  792. | DataConWrapId dc <- idDetails i, isNewTyCon (dataConTyCon dc) = do
  793. as <- concatMapM genArg a
  794. case as of
  795. [ai] -> do
  796. let [t] = concatMap snd (top ^. ctxTarget)
  797. [StgVarArg a'] = a
  798. if isStrictId a' || a' `elementOfUniqSet` (top ^. ctxEval)
  799. then return ([j| `t` = `ai`; |], ExprInline Nothing)
  800. else return ([j| return h$e(`ai`); |], ExprCont)
  801. _ -> panic "genApp: invalid size"
  802. | idFunRepArity i == 0 && n == 0 && not (might_be_a_function (idType i)) = do
  803. ii <- enterId
  804. return ([j| return h$e(`ii`) |], ExprCont)
  805. | idFunRepArity i == n && not (isLocalId i) && isStrictId i && n /= 0 = do
  806. as' <- concatMapM genArg a
  807. jmp <- jumpToII i as' =<< r1
  808. return (jmp, ExprCont)
  809. | idFunRepArity i < n && isStrictId i && idFunRepArity i > 0 =
  810. let (reg,over) = splitAt (idFunRepArity i) a
  811. in do
  812. reg' <- concatMapM genArg reg
  813. pc <- pushCont over
  814. jmp <- jumpToII i reg' =<< r1
  815. return (pc <> jmp, ExprCont)
  816. | otherwise = do
  817. jmp <- jumpToFast a =<< r1
  818. return (jmp, ExprCont)
  819. where
  820. enterId :: G JExpr
  821. enterId = genArg (StgVarArg i) >>=
  822. \case
  823. [x] -> return x
  824. xs -> panic $ "genApp: unexpected multi-var argument (" ++ show (length xs) ++ ")\n" ++ showIndent i
  825. r1 :: C
  826. r1 = do
  827. ids <- genIds i
  828. return $ mconcat $ zipWith (\r u -> [j| `r`=`u`; |]) (enumFrom R1) ids
  829. n = length a
  830. pushCont :: HasDebugCallStack
  831. => [StgArg]
  832. -> C
  833. pushCont as = do
  834. as' <- concatMapM genArg as
  835. (app, spec) <- selectApply False (as,as')
  836. if spec
  837. then push $ reverse $ app : as'
  838. else push $ reverse $ app : mkTag as' as : as'
  839. where
  840. mkTag rs ns = toJExpr ((length rs `shiftL` 8) .|. length ns)
  841. -- regular let binding: allocate heap object
  842. genBind :: HasDebugCallStack
  843. => ExprCtx
  844. -> StgBinding
  845. -> G (JStat, ExprCtx)
  846. genBind ctx bndr =
  847. case bndr of
  848. StgNonRec b r -> do
  849. j <- assign b r >>= \case
  850. Just ja -> return ja
  851. Nothing -> allocCls Nothing [(b,r)]
  852. return (j, addEvalRhs ctx [(b,r)])
  853. StgRec bs -> do
  854. jas <- mapM (uncurry assign) bs -- fixme these might depend on parts initialized by allocCls
  855. let m = if null jas then Nothing else Just (mconcat $ catMaybes jas)
  856. j <- allocCls m . map snd . filter (isNothing . fst) $ zip jas bs
  857. return (j, addEvalRhs ctx bs)
  858. where
  859. ctx' = clearCtxStack ctx
  860. assign :: Id -> StgRhs -> G (Maybe JStat)
  861. assign b (StgRhsClosure _ccs _bi [the_fv] _upd [] expr)
  862. | let strip = snd . stripStgTicksTop (not . tickishIsCode)
  863. , StgCase (StgApp scrutinee []) _ (AlgAlt _) [(DataAlt _, params, sel_expr)] <- strip expr
  864. , StgApp selectee [] <- strip sel_expr
  865. , let params_w_offsets = zip params (scanl' (+) 1 $ map (typeSize . idType) params)
  866. , let total_size = sum (map (typeSize . idType) params)
  867. , the_fv == scrutinee
  868. , Just the_offset <- assocMaybe params_w_offsets selectee
  869. , the_offset <= 16 -- fixme make this some configurable constant
  870. = do
  871. let sel_tag | the_offset == 2 = if total_size == 2 then "2a"
  872. else "2b"
  873. | otherwise = show the_offset
  874. tgts <- genIdsI b
  875. the_fvjs <- genIds the_fv
  876. case (tgts, the_fvjs) of
  877. ([tgt], [the_fvj]) -> return $ Just
  878. (tgt ||= ApplExpr (ValExpr (JVar (TxtI ("h$c_sel_" <> T.pack sel_tag))))
  879. [the_fvj])
  880. _ -> panic "genBind.assign: invalid size"
  881. assign b (StgRhsClosure _ccs _bi _free _upd [] expr)
  882. | snd (isInlineExpr (ctx ^. ctxEval) expr) = do
  883. d <- declIds b
  884. tgt <- genIds b
  885. (j, _) <- genExpr (ctx & ctxTarget .~ alignTarget (idTarget b) tgt) expr
  886. return (Just (d <> j))
  887. assign b (StgRhsCon{}) = return Nothing
  888. assign b r = genEntry ctx' b r >> return Nothing
  889. addEvalRhs c [] = c
  890. addEvalRhs c ((b,r):xs)
  891. | (StgRhsCon{}) <- r = addEvalRhs (addEval b c) xs
  892. | (StgRhsClosure _ _ _ ReEntrant _ _) <- r = addEvalRhs (addEval b c) xs
  893. | otherwise = addEvalRhs c xs
  894. genBindLne :: HasDebugCallStack
  895. => ExprCtx
  896. -> StgBinding
  897. -> G (JStat, ExprCtx)
  898. genBindLne ctx bndr =
  899. trace' ("genBindLne\n" ++ showIndent bndr)
  900. (genBindLne0 ctx bndr)
  901. genBindLne0 :: HasDebugCallStack
  902. => ExprCtx
  903. -> StgBinding
  904. -> G (JStat, ExprCtx)
  905. genBindLne0 ctx bndr = do
  906. vis <- map (\(x,y,_) -> (x,y)) <$>
  907. optimizeFree oldFrameSize (newLvs++map fst updBinds)
  908. declUpds <- mconcat <$> mapM (fmap (||= jnull) . jsIdI . fst) updBinds
  909. let newFrameSize = oldFrameSize + length vis
  910. ctx' = ctx & ctxLne %~ flip addListToUniqSet bound
  911. & ctxLneFrameBs %~ flip addListToUFM (map (,newFrameSize) bound)
  912. & ctxLneFrame %~ (++vis)
  913. mapM_ (uncurry $ genEntryLne ctx') binds
  914. return (declUpds, ctx')
  915. where
  916. oldFrame = ctx ^. ctxLneFrame
  917. oldFrameSize = length oldFrame
  918. isOldLv i = i `elementOfUniqSet` (ctx ^. ctxLne) ||
  919. i `elem` (map fst oldFrame)
  920. live = liveVars $ mkDVarSet $ stgLneLive' bndr
  921. newLvs = filter (not . isOldLv) (dVarSetElems live)
  922. binds = case bndr of
  923. StgNonRec b e -> [(b,e)]
  924. StgRec bs -> bs
  925. bound = map fst binds
  926. (updBinds, _nonUpdBinds) = partition (isUpdatableRhs . snd) binds
  927. stgLneLive' :: StgBinding -> [Id]
  928. stgLneLive' b = filter (`notElem` bindees b) (stgLneLive b)
  929. stgLneLive :: StgBinding -> [Id]
  930. stgLneLive (StgNonRec _b e) = stgLneLiveExpr e
  931. stgLneLive (StgRec bs) = L.nub $ concatMap (stgLneLiveExpr . snd) bs
  932. stgLneLiveExpr :: StgRhs -> [Id]
  933. stgLneLiveExpr (StgRhsClosure _ _ l _ _ _) = l
  934. stgLneLiveExpr (StgRhsCon {}) = []
  935. isUpdatableRhs :: StgRhs -> Bool
  936. isUpdatableRhs (StgRhsClosure _ _ _ u _ _) = isUpdatable u
  937. isUpdatableRhs _ = False
  938. {-
  939. Let-no-escape entries live on the stack. There is no heap object associated with them.
  940. A let-no-escape entry is called like a normal stack frame, although as an optimization,
  941. `Stack`[`Sp`] is not set when making the call. This done later if the thread needs to
  942. be suspended.
  943. Updatable let-no-escape binders have one 'private' slot in the stack frame. This slot
  944. is initially set to null, changed to h$blackhole when the thunk is being evaluated.
  945. -}
  946. genEntryLne :: HasDebugCallStack => ExprCtx -> Id -> StgRhs -> G ()
  947. genEntryLne ctx i rhs@(StgRhsClosure _cc _bi _live2 update args body) =
  948. resetSlots $ do
  949. let payloadSize = length frame
  950. frame = ctx ^. ctxLneFrame
  951. myOffset =
  952. maybe (panic "genEntryLne: updatable binder not found in let-no-escape frame")
  953. ((payloadSize-) . fst)
  954. (listToMaybe $ filter ((==i).fst.snd) (zip [0..] frame))
  955. bh | isUpdatable update =
  956. [j| var x = h$bh_lne(`Sp`-`myOffset`, `payloadSize+1`);
  957. if(x) return(x);
  958. |]
  959. | otherwise = mempty
  960. lvs <- popLneFrame True payloadSize ctx
  961. body <- genBody ctx i R1 args body
  962. ei <- jsEntryIdI i
  963. sr <- genStaticRefsRhs rhs
  964. let f = JFunc [] (bh <> lvs <> body)
  965. emitClosureInfo $
  966. ClosureInfo (itxt ei)
  967. (CIRegs 0 $ concatMap idVt args)
  968. (itxt ei <> ", " <> T.pack (show i))
  969. (fixedLayout . reverse $
  970. map (stackSlotType . fst) (ctx ^. ctxLneFrame))
  971. CIStackFrame
  972. sr
  973. emitToplevel (ei ||= f)
  974. genEntryLne ctx i (StgRhsCon cc con args) = resetSlots $ do
  975. let payloadSize = length (ctx ^. ctxLneFrame)
  976. ei <- jsEntryIdI i
  977. di <- enterDataCon con
  978. ii <- makeIdent
  979. p <- popLneFrame True payloadSize ctx
  980. args' <- concatMapM genArg args
  981. ac <- allocCon ii con cc args'
  982. emitToplevel $ ei ||= JFunc []
  983. (decl ii <> p <> ac <> [j| `R1` = `ii`; |] <> returnStack)
  984. -- generate the entry function for a local closure
  985. genEntry :: HasDebugCallStack => ExprCtx -> Id -> StgRhs -> G ()
  986. genEntry _ _i (StgRhsCon _cc _con _args) = return () -- mempty -- error "local data entry"
  987. genEntry ctx i rhs@(StgRhsClosure cc _bi live upd_flag args body) = resetSlots $ do
  988. ll <- loadLiveFun live
  989. llv <- verifyRuntimeReps live
  990. upd <- genUpdFrame upd_flag i
  991. body <- genBody entryCtx i R2 args body
  992. ei <- jsEntryIdI i
  993. et <- genEntryType args
  994. setcc <- ifProfiling $
  995. if et == CIThunk
  996. then enterCostCentreThunk
  997. else enterCostCentreFun cc
  998. sr <- genStaticRefsRhs rhs
  999. emitClosureInfo $ ClosureInfo (itxt ei)
  1000. (CIRegs 0 $ PtrV : concatMap idVt args)
  1001. (itxt ei <> ", " <> T.pack (show i))
  1002. (fixedLayout $ map (uTypeVt . idType) live)
  1003. et
  1004. sr
  1005. emitToplevel (ei ||= JFunc [] (ll <> llv <> upd <> setcc <> body))
  1006. where
  1007. entryCtx = ExprCtx i [] (ctx ^. ctxEval) (ctx ^. ctxLne) emptyUFM [] (ctx ^. ctxSrcSpan)
  1008. genEntryType :: HasDebugCallStack => [Id] -> G CIType
  1009. genEntryType [] = return CIThunk
  1010. genEntryType args0 = {- trace' "genEntryType" $ -} do
  1011. args' <- mapM genIdArg args
  1012. return $ CIFun (length args) (length $ concat args')
  1013. where
  1014. args = filter (not . isRuntimeRepKindedTy . idType) args0
  1015. genSetConInfo :: HasDebugCallStack => Id -> DataCon -> LiveVars -> C
  1016. genSetConInfo i d l {- srt -} = do
  1017. ei <- jsDcEntryIdI i
  1018. sr <- genStaticRefs l
  1019. emitClosureInfo $ ClosureInfo (itxt ei)
  1020. (CIRegs 0 [PtrV])
  1021. (T.pack $ show d)
  1022. (fixedLayout $ map uTypeVt fields)
  1023. (CICon $ dataConTag d)
  1024. sr
  1025. return (ei ||= mkDataEntry)
  1026. where
  1027. -- dataConRepArgTys sometimes returns unboxed tuples. is that a bug?
  1028. fields = concatMap (map primRepToType . typePrimRep . unwrapType)
  1029. (dataConRepArgTys d)
  1030. -- concatMap (map slotTyToType . repTypeSlots . repType) (dataConRepArgTys d)
  1031. mkDataEntry :: JExpr
  1032. mkDataEntry = ValExpr $ JFunc [] returnStack
  1033. genUpdFrame :: UpdateFlag -> Id -> C
  1034. genUpdFrame u i
  1035. | isReEntrant u = mempty
  1036. | isOneShotBndr i = maybeBh
  1037. | isUpdatable u = updateThunk
  1038. | otherwise = maybeBh
  1039. where
  1040. isReEntrant ReEntrant = True
  1041. isReEntrant _ = False
  1042. maybeBh = do
  1043. settings <- use gsSettings
  1044. assertRtsStat (return $ bhSingleEntry settings)
  1045. -- allocate local closures
  1046. allocCls :: Maybe JStat -> [(Id, StgRhs)] -> C
  1047. allocCls dynMiddle xs = do
  1048. (stat, dyn) <- partitionEithers <$> mapM toCl xs
  1049. cs <- use gsSettings
  1050. return (mconcat stat) <> allocDynAll cs True dynMiddle dyn
  1051. where
  1052. -- left = static, right = dynamic
  1053. toCl :: (Id, StgRhs)
  1054. -> G (Either JStat (Ident,JExpr,[JExpr],CostCentreStack))
  1055. -- statics
  1056. {- making zero-arg constructors static is problematic, see #646
  1057. proper candidates for this optimization should have been floated
  1058. already
  1059. toCl (i, StgRhsCon cc con []) = do
  1060. ii <- jsIdI i
  1061. Left <$> (return (decl ii) <> allocCon ii con cc []) -}
  1062. toCl (i, StgRhsCon cc con [a]) | isUnboxableCon con = do
  1063. ii <- jsIdI i
  1064. Left <$> (return (decl ii) <> (allocCon ii con cc =<< genArg a))
  1065. -- dynamics
  1066. toCl (i, StgRhsCon cc con ar) =
  1067. -- fixme do we need to handle unboxed?
  1068. Right <$> ((,,,) <$> jsIdI i
  1069. <*> enterDataCon con
  1070. <*> concatMapM genArg ar
  1071. <*> pure cc)
  1072. toCl (i, StgRhsClosure cc _bi live _upd_flag _args _body) =
  1073. Right <$> ((,,,) <$> jsIdI i
  1074. <*> jsEntryId i
  1075. <*> concatMapM genIds live
  1076. <*> pure cc)
  1077. genCase :: HasDebugCallStack
  1078. => ExprCtx
  1079. -> Id
  1080. -> StgExpr
  1081. -> AltType
  1082. -> [StgAlt]
  1083. -> LiveVars
  1084. -> G (JStat, ExprResult)
  1085. genCase top bnd e at alts l =
  1086. trace' ("genCase\n" ++ showIndent e ++ "\n" ++ unlines (map showIndent alts))
  1087. (genCase0 top bnd e at alts l)
  1088. -- fixme CgCase has a reps_compatible check here
  1089. genCase0 :: HasDebugCallStack
  1090. => ExprCtx
  1091. -> Id
  1092. -> StgExpr
  1093. -> AltType
  1094. -> [StgAlt]
  1095. -> LiveVars
  1096. -> G (JStat, ExprResult)
  1097. genCase0 top bnd e at alts l
  1098. | snd (isInlineExpr (top ^. ctxEval) e) = withNewIdent $ \ccsVar -> do
  1099. bndi <- genIdsI bnd
  1100. (ej, r) <- genExpr (top & ctxTop .~ bnd
  1101. & ctxTarget .~ alignTarget (idTarget bnd)
  1102. (map toJExpr bndi)) e
  1103. -- ExprCtx bnd (map toJExpr bndi) (top ^. ctxEval) (top ^. ctxLneV) (top ^. ctxLneB) (top ^. ctxLne)) e
  1104. let d = case r of
  1105. ExprInline d0 -> d0
  1106. ExprCont -> panic $ "genCase: expression was not inline:\n" ++
  1107. showIndent e ++ "\n" ++
  1108. (TL.unpack . (<>"\n") . displayT . renderPretty 0.8 150 . pretty . jsSaturate (Just "debug") $ ej)
  1109. ww = mempty -- if snd (isInlineExpr emptyUniqSet e) then mempty else [j| h$log('danger will robinson'); |]
  1110. (aj, ar) <- genAlts (addEval bnd top) bnd at d alts
  1111. saveCCS <- ifProfiling $ ccsVar |= jCurrentCCS
  1112. restoreCCS <- ifProfiling $ [j| `jCurrentCCS` = `ccsVar`; |]
  1113. return ( decl ccsVar <>
  1114. mconcat (map decl bndi) <>
  1115. saveCCS <>
  1116. ww <>
  1117. ej <>
  1118. restoreCCS <>
  1119. aj
  1120. , ar
  1121. )
  1122. | otherwise = do
  1123. rj <- genRet (addEval bnd top) bnd at alts l
  1124. (ej, _r) <- genExpr (top & ctxTop .~ bnd
  1125. & ctxTarget .~ alignTarget (idTarget bnd)
  1126. (map toJExpr [R1 ..])) e
  1127. return (rj <> ej, ExprCont)
  1128. alignTarget :: [(PrimRep, Int)] -> [a] -> [(PrimRep, [a])]
  1129. alignTarget [] _ = []
  1130. alignTarget ((rep, size):xs) vs
  1131. | length vs0 == size = (rep, vs0) : alignTarget xs vs1
  1132. | otherwise = panic "alignTarget: target size insufficient"
  1133. where (vs0, vs1) = splitAt size vs
  1134. idTarget :: Id -> [(PrimRep, Int)]
  1135. idTarget = typeTarget . idType
  1136. typeTarget :: Type -> [(PrimRep, Int)]
  1137. typeTarget = map (\t -> (t, varSize (primRepVt t))) . typePrimRep . unwrapType
  1138. assignAll :: (ToJExpr a, ToJExpr b) => [a] -> [b] -> JStat
  1139. assignAll xs ys = mconcat (zipWith assignj xs ys)
  1140. -- assign ys to xs, checking if the lengths are compatible
  1141. assignAllCh :: (ToJExpr a, ToJExpr b) => String -> [a] -> [b] -> JStat
  1142. assignAllCh msg xs ys
  1143. | length xs == length ys = mconcat (zipWith assignj xs ys)
  1144. | otherwise =
  1145. panic $ "assignAllCh: lengths do not match: " ++
  1146. show (length xs, length ys) ++
  1147. "\n " ++
  1148. msg
  1149. assignAllCh1 :: String
  1150. -> [(PrimRep, [JExpr])]
  1151. -> [(PrimRep, [JExpr])]
  1152. -> JStat
  1153. assignAllCh1 msg ((rx,ex):xs) ((ry,ey):ys) =
  1154. assignPrimReps rx ry ex ey
  1155. assignAllCh1 _ [] [] = mempty
  1156. assignAllCh1 _ _ _ =
  1157. panic $ "assignAllCh1: lengths do not match"
  1158. -- assign p2 to p1
  1159. assignPrimReps :: PrimRep -> PrimRep -> [JExpr] -> [JExpr] -> JStat
  1160. assignPrimReps p1 p2 e1 e2
  1161. -- Allow same size assignment, even if rep is not the same
  1162. -- | p1 /= p2 && Debug.Trace.trace ("implicit conversion: " ++ show p2 ++ " -> " ++ show p1) False = undefined
  1163. | length e1 == length e2 = mconcat (zipWith assignj e1 e2)
  1164. -- Coercion between StablePtr# and Addr#
  1165. assignPrimReps AddrRep UnliftedRep [a_val, a_off] [sptr] =
  1166. [j| `a_val` = h$stablePtrBuf; `a_off` = `sptr`; |]
  1167. assignPrimReps UnliftedRep AddrRep [sptr] [a_val, a_off] =
  1168. [j| `sptr` = `a_off`; |]
  1169. assignPrimReps p1 p2 e1 e2 =
  1170. let sr r s = show r ++ " (size " ++ show (length s) ++ ")"
  1171. in panic $ "cannot assign " ++ sr p2 e2 ++ " to " ++ sr p1 e1
  1172. genRet :: HasDebugCallStack
  1173. => ExprCtx
  1174. -> Id
  1175. -> AltType
  1176. -> [StgAlt]
  1177. -> LiveVars
  1178. -> C
  1179. genRet ctx e at as l = -- withNewIdent f
  1180. trace' ("genRet" ++ unlines (map showIndent as))
  1181. (genRet0 ctx e at as l)
  1182. genRet0 :: HasDebugCallStack
  1183. => ExprCtx
  1184. -> Id
  1185. -> AltType
  1186. -> [StgAlt]
  1187. -> LiveVars
  1188. -> C
  1189. genRet0 ctx e at as l = withNewIdent f
  1190. where
  1191. allRefs :: [Id]
  1192. allRefs = S.toList . S.unions $ as ^.. traverse . _3 . to (exprRefs emptyUFM)
  1193. lneLive :: Int
  1194. lneLive = maximum $ 0 : map (fromMaybe 0 . lookupUFM (ctx ^. ctxLneFrameBs)) allRefs
  1195. ctx' = adjustCtxStack lneLive ctx
  1196. lneVars = map fst $ take lneLive (ctx ^. ctxLneFrame)
  1197. isLne i = i `elem` lneVars || i `elementOfUniqSet` (ctx ^. ctxLne)
  1198. nonLne = filter (not . isLne) (dVarSetElems l)
  1199. f :: Ident -> C
  1200. f r = do
  1201. pushLne <- pushLneFrame lneLive ctx
  1202. saveCCS <- ifProfilingM $ push [jCurrentCCS]
  1203. free <- trace' ("nonLne: " ++ show nonLne) (optimizeFree 0 nonLne)
  1204. pushRet <- pushRetArgs free (iex r)
  1205. fun' <- fun free
  1206. sr <- genStaticRefs l -- srt
  1207. prof <- profiling
  1208. emitClosureInfo $
  1209. ClosureInfo (itxt r)
  1210. (CIRegs 0 altRegs)
  1211. (itxt r)
  1212. (fixedLayout . reverse $
  1213. map (stackSlotType . fst3) free
  1214. ++ if prof then [ObjV] else []
  1215. ++ map stackSlotType lneVars)
  1216. CIStackFrame
  1217. sr
  1218. emitToplevel $ r ||= JFunc [] fun'
  1219. return (pushLne <> saveCCS <> pushRet)
  1220. fst3 ~(x,_,_) = x
  1221. altRegs :: HasDebugCallStack => [VarType]
  1222. altRegs = case at of
  1223. PrimAlt ptc -> [primRepVt ptc]
  1224. MultiValAlt _n -> idVt e
  1225. _ -> [PtrV]
  1226. fun free = resetSlots $ do
  1227. decs <- declIds e
  1228. load <- flip assignAll [R1 ..] <$> genIdsI e
  1229. loadv <- verifyRuntimeReps [e]
  1230. ras <- loadRetArgs free
  1231. rasv <- verifyRuntimeReps (map (\(x,_,_)->x) free)
  1232. restoreCCS <- ifProfilingM $ popUnknown [jCurrentCCS]
  1233. rlne <- popLneFrame False lneLive ctx'
  1234. rlnev <- verifyRuntimeReps (map fst $ take lneLive (ctx' ^. ctxLneFrame))
  1235. (alts, _altr) <- genAlts ctx' e at Nothing as
  1236. return $ decs <> load <> loadv <> ras <> rasv <> restoreCCS <> rlne <> rlnev <> alts <>
  1237. returnStack
  1238. -- 2-var values might have been moved around separately, use DoubleV as substitute
  1239. -- ObjV is 1 var, so this is no problem for implicit metadata
  1240. stackSlotType :: Id -> VarType
  1241. stackSlotType i
  1242. | varSize otype == 1 = otype
  1243. | otherwise = DoubleV
  1244. where otype = uTypeVt (idType i)
  1245. popLneFrame :: Bool -> Int -> ExprCtx -> C
  1246. popLneFrame inEntry size ctx
  1247. | l < size = panic $ "popLneFrame: let-no-escape frame too short: " ++
  1248. show l ++ " < " ++ show size
  1249. | otherwise = popSkipI skip
  1250. =<< mapM (\(i,n) -> (,SlotId i n) <$> genIdsIN i n)
  1251. (take size $ ctx ^. ctxLneFrame)
  1252. where
  1253. skip = if inEntry then 1 else 0 -- pop the frame header
  1254. l = ctx ^. ctxLneFrame . to length
  1255. pushLneFrame :: HasDebugCallStack => Int -> ExprCtx -> C
  1256. pushLneFrame size ctx
  1257. | l < size = panic $ "pushLneFrame: let-no-escape frame too short " ++
  1258. show l ++ " < " ++ show size
  1259. | otherwise = pushOptimized' (take size $ ctx ^. ctxLneFrame)
  1260. where
  1261. l = ctx ^. ctxLneFrame . to length
  1262. -- reorder the things we need to push to reuse existing stack values as much as possible
  1263. -- True if already on the stack at that location
  1264. optimizeFree :: HasDebugCallStack => Int -> [Id] -> G [(Id,Int,Bool)]
  1265. optimizeFree offset ids = do
  1266. -- this line goes wrong vvvvvvv
  1267. let -- ids' = concat $ map (\i -> map (i,) [1..varSize . uTypeVt . idType $ i]) ids
  1268. idSize :: Id -> Int
  1269. idSize i = let s = idSize0 i in trace' ("idSize: " ++ show i ++ " -> " ++ show s) s
  1270. idSize0 :: Id -> Int
  1271. idSize0 i = sum $ map varSize (typeVt . idType $ i)
  1272. ids' = concat $ map (\i -> map (i,) [1..idSize i]) ids
  1273. -- 1..varSize] . uTypeVt . idType $ i]) (typeVt ids)
  1274. l = length ids'
  1275. slots <- drop offset . take l . (++repeat SlotUnknown) <$> getSlots
  1276. let slm = M.fromList (zip slots [0..])
  1277. (remaining, fixed) = partitionEithers $
  1278. map (\inp@(i,n) -> maybe (Left inp) (\j -> Right (i,n,j,True))
  1279. (M.lookup (SlotId i n) slm)) ids'
  1280. takenSlots = S.fromList (fixed ^.. traverse . _3)
  1281. freeSlots = filter (`S.notMember` takenSlots) [0..l-1]
  1282. remaining' = zipWith (\(i,n) j -> (i,n,j,False)) remaining freeSlots
  1283. allSlots = sortBy (compare `on` \(_,_,x,_) -> x) (fixed ++ remaining')
  1284. return $ map (\(i,n,_,b) -> (i,n,b)) allSlots
  1285. (!!!) :: HasDebugCallStack => [a] -> Int -> a
  1286. xs !!! n = case (drop n xs) of
  1287. x:_ -> x
  1288. _ -> error "list too short"
  1289. pushRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> JExpr -> C
  1290. pushRetArgs free fun = do
  1291. p <- pushOptimized . (++[(fun,False)]) =<< mapM (\(i,n,b) -> (\es->(es!!(n-1),b)) <$> genIdArg i) free
  1292. return p
  1293. loadRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> C
  1294. loadRetArgs free = popSkipI 1 =<< ids
  1295. where
  1296. ids = mapM (\(i,n,_b) -> (!!!(n-1)) <$> genIdStackArgI i) free
  1297. genAlts :: HasDebugCallStack
  1298. => ExprCtx -- ^ lhs to assign expression result to
  1299. -> Id -- ^ id being matched
  1300. -> AltType -- ^ type
  1301. -> Maybe [JExpr] -- ^ if known, fields in datacon from earlier expression
  1302. -> [StgAlt] -- ^ the alternatives
  1303. -> G (JStat, ExprResult)
  1304. genAlts top e at me as =
  1305. trace''
  1306. ("genAlts0\n" ++ unlines ([{- show top, -} show e, show at] ++ map show as)) $ do
  1307. ver <- verifyMatchRep e at
  1308. (st, er) <- genAlts0 top e at me as
  1309. pure (ver <> st, er)
  1310. --(\(_,s,r) -> (s,r)) <$> mkAlgBranch top e alt
  1311. genAlts0 :: HasDebugCallStack
  1312. => ExprCtx -- ^ lhs to assign expression result to
  1313. -> Id -- ^ id being matched
  1314. -> AltType -- ^ type
  1315. -> Maybe [JExpr] -- ^ if known, fields in datacon from earlier expression
  1316. -> [StgAlt] -- ^ the alternatives
  1317. -> G (JStat, ExprResult)
  1318. genAlts0 top e PolyAlt _ [alt] = (\(_,s,r) -> (s,r)) <$> mkAlgBranch top e alt
  1319. genAlts0 _ _ PolyAlt _ _ = panic "genAlts: multiple polyalt"
  1320. genAlts0 top e (PrimAlt _tc) _ [(_, bs, expr)] = do
  1321. ie <- genIds e
  1322. dids <- mconcat (map declIds bs)
  1323. bss <- concatMapM genIds bs
  1324. (ej, er) <- genExpr top expr
  1325. return (dids <> assignAll bss ie <> ej, er)
  1326. genAlts0 top e (PrimAlt tc) _ alts = do
  1327. ie <- genIds e
  1328. (r, bss) <- normalizeBranches top <$>
  1329. mapM (isolateSlots . mkPrimIfBranch top [primRepVt tc]) alts
  1330. setSlots []
  1331. return (mkSw ie bss, r)
  1332. genAlts0 top e (MultiValAlt n) _ [(_, bs, expr)] = do
  1333. eids <- genIds e
  1334. l <- loadUbxTup eids bs n
  1335. (ej, er) <- genExpr top expr
  1336. return (l <> ej, er)
  1337. genAlts0 _ _ (AlgAlt tc) _ [_alt] | isUnboxedTupleTyCon tc =
  1338. panic "genAlts: unexpected unboxed tuple"
  1339. genAlts0 top _ (AlgAlt _tc) (Just es) [(DataAlt dc, bs, expr)]
  1340. | not (isUnboxableCon dc) = do
  1341. bsi <- mapM genIdsI bs
  1342. let args = zipWith (\i de -> i ||= de) (concat bsi) es
  1343. (ej, er) <- genExpr top expr
  1344. return (mconcat args <> ej, er)
  1345. genAlts0 top e (AlgAlt _tc) _ [alt] = do
  1346. (_,s,r) <- mkAlgBranch top e alt
  1347. return (s, r)
  1348. genAlts0 top e (AlgAlt _tc) _ alts@[(DataAlt dc,_,_),_]
  1349. | isBoolTy (dataConType dc) = do
  1350. i <- jsId e
  1351. nbs <- normalizeBranches top <$>
  1352. mapM (isolateSlots . mkAlgBranch top e) alts
  1353. case nbs of
  1354. (r, [(_,s1,_), (_,s2,_)]) -> do
  1355. let s = if dataConTag dc == 2
  1356. then [j| if(`i`) { `s1` } else { `s2` } |]
  1357. else [j| if(`i`) { `s2` } else { `s1` } |]
  1358. setSlots []
  1359. return (s, r)
  1360. _ -> error "genAlts: invalid branches for Bool"
  1361. -- fixme, add all alts
  1362. genAlts0 top e (AlgAlt _tc) _ alts = do
  1363. ei <- jsId e
  1364. (r, brs) <- normalizeBranches top <$>
  1365. mapM (isolateSlots . mkAlgBranch top e) alts
  1366. setSlots []
  1367. return (mkSwitch [je| `ei`.f.a |] brs, r)
  1368. genAlts0 _ _ a _ l = do
  1369. ap <- showPpr' a
  1370. panic $ "genAlts: unhandled case variant: " ++
  1371. ap ++
  1372. " (" ++
  1373. show (length l) ++
  1374. ")"
  1375. -- if one branch ends in a continuation but another is inline,
  1376. -- we need to adjust the inline branch to use the continuation convention
  1377. normalizeBranches :: ExprCtx
  1378. -> [(a, JStat, ExprResult)]
  1379. -> (ExprResult, [(a, JStat, ExprResult)])
  1380. normalizeBranches e brs
  1381. | all (==ExprCont) (brs ^.. traverse . _3) =
  1382. (ExprCont, brs)
  1383. | branchResult (brs ^.. traverse ._3) == ExprCont =
  1384. (ExprCont, map mkCont brs)
  1385. | otherwise =
  1386. (ExprInline Nothing, brs)
  1387. where
  1388. mkCont (me, s, ExprInline{}) = ( me
  1389. , s <> assignAll (enumFrom R1)
  1390. (concatMap snd $ e ^. ctxTarget)
  1391. , ExprCont)
  1392. mkCont x = x
  1393. loadUbxTup :: [JExpr] -> [Id] -> Int -> C
  1394. loadUbxTup es bs _n = do
  1395. bs' <- concatMapM genIdsI bs
  1396. return $ mconcat $ zipWith (||=) bs' es
  1397. mkSw :: [JExpr] -> [(Maybe [JExpr], JStat, ExprResult)] -> JStat
  1398. mkSw [e] cases = mkSwitch e (over (mapped._1.mapped) head cases)
  1399. mkSw es cases = mkIfElse es cases
  1400. -- switch for pattern matching on constructors or prims
  1401. mkSwitch :: JExpr -> [(Maybe JExpr, JStat, ExprResult)] -> JStat
  1402. mkSwitch e cases
  1403. | [(Just c1,s1,_)] <- n, [(_,s2,_)] <- d =
  1404. IfStat [je| `e` === `c1` |] s1 s2
  1405. | [(Just c1,s1,_),(_,s2,_)] <- n, null d =
  1406. IfStat [je| `e` === `c1` |] s1 s2
  1407. | null d =
  1408. SwitchStat e (map addBreak (init n)) (last n ^. _2)
  1409. | [(_,d0,_)] <- d =
  1410. SwitchStat e (map addBreak n) d0
  1411. | otherwise = panic "mkSwitch: multiple default cases"
  1412. where
  1413. addBreak (Just c, s, _) = (c, s <> [j| break; |])
  1414. addBreak _ = panic "mkSwitch: addBreak"
  1415. (n,d) = partition (isJust . (^. _1)) cases
  1416. -- if/else for pattern matching on things that js cannot switch on
  1417. mkIfElse :: [JExpr] -> [(Maybe [JExpr], JStat, ExprResult)] -> JStat
  1418. mkIfElse e s = go (reverse $ sort s)
  1419. where
  1420. go [] = panic "mkIfElse: empty expression list"
  1421. go [(_, s, _)] = s -- only one 'nothing' allowed
  1422. go ((Just e0, s, _):xs) =
  1423. [j| if( `mkEq e e0` ) { `s` } else { `go xs` } |]
  1424. go _ = panic "mkIfElse: multiple DEFAULT cases"
  1425. mkEq :: [JExpr] -> [JExpr] -> JExpr
  1426. mkEq es1 es2
  1427. | length es1 == length es2 = foldl1 and (zipWith eq es1 es2)
  1428. | otherwise = panic "mkEq: incompatible expressions"
  1429. where
  1430. and e1 e2 = [je| `e1` && `e2` |]
  1431. eq e1 e2 = [je| `e1` === `e2` |]
  1432. mkAlgBranch :: ExprCtx -- ^ toplevel id for the result
  1433. -> Id -- ^ datacon to match
  1434. -> StgAlt -- ^ match alternative with binders
  1435. -> G (Maybe JExpr, JStat, ExprResult)
  1436. mkAlgBranch top d (DataAlt dc,[b],expr)
  1437. | isUnboxableCon dc = do
  1438. idd <- jsId d
  1439. fldx <- genIdsI b
  1440. case fldx of
  1441. [fld] -> do
  1442. (ej, er) <- genExpr top expr
  1443. return (Nothing, decl fld <> [j| `fld` = `idd` |] <> ej, er)
  1444. _ -> panic "mkAlgBranch: invalid size"
  1445. mkAlgBranch top d (a, bs, expr) = do
  1446. cc <- caseCond a
  1447. idd <- jsId d
  1448. b <- loadParams idd bs
  1449. (ej, er) <- genExpr top expr
  1450. return (cc, b <> ej, er)
  1451. mkPrimIfBranch :: ExprCtx
  1452. -> [VarType]
  1453. -> StgAlt
  1454. -> G (Maybe [JExpr], JStat, ExprResult)
  1455. mkPrimIfBranch top _vt (cond, _, e) =
  1456. (\ic (ej,er) -> (ic,ej,er)) <$> ifCond cond <*> genExpr top e
  1457. -- fixme are bool things always checked correctly here?
  1458. ifCond :: AltCon -> G (Maybe [JExpr])
  1459. ifCond (DataAlt da) = return $ Just [[je| `dataConTag da` |]]
  1460. ifCond (LitAlt l) = Just <$> genLit l
  1461. ifCond DEFAULT = return Nothing
  1462. caseCond :: AltCon -> G (Maybe JExpr)
  1463. caseCond (DataAlt da) = return $ Just [je| `dataConTag da` |]
  1464. caseCond (LitAlt l) = Just <$> genSingleLit l
  1465. caseCond DEFAULT = return Nothing
  1466. -- load parameters from constructor
  1467. -- fixme use single tmp var for all branches
  1468. loadParams :: JExpr -> [Id] -> C
  1469. loadParams from args = do
  1470. as <- concat <$> sequence (zipWith (\a u -> map (,u) <$> genIdsI a) args use)
  1471. return $ case as of
  1472. [] -> mempty
  1473. [(x,u)] -> loadIfUsed [je| `from`.d1 |] x u
  1474. [(x1,u1),(x2,u2)] -> loadIfUsed [je| `from`.d1 |] x1 u1 <>
  1475. loadIfUsed [je| `from`.d2 |] x2 u2
  1476. ((x,u):xs) -> loadIfUsed [je| `from`.d1 |] x u <>
  1477. [j| var d = `from`.d2;
  1478. `loadConVarsIfUsed d xs`;
  1479. |]
  1480. where
  1481. use = repeat True -- fixme clean up
  1482. loadIfUsed fr tgt True = decl' tgt fr
  1483. loadIfUsed _ _ _ = mempty
  1484. loadConVarsIfUsed fr cs = mconcat $ zipWith f cs [(1::Int)..]
  1485. where f (x,u) n = loadIfUsed (SelExpr fr (dataFields ! n)) x u
  1486. genPrimOp :: ExprCtx -> PrimOp -> [StgArg] -> Type -> G (JStat, ExprResult)
  1487. genPrimOp top op args t = do
  1488. as <- concatMapM genArg args
  1489. df <- use gsDynFlags
  1490. -- fixme: should we preserve/check the primreps?
  1491. return $ case genPrim df t op (map toJExpr . concatMap snd $ top ^. ctxTarget) as of
  1492. PrimInline s -> (s, ExprInline Nothing)
  1493. PRPrimCall s -> (s, ExprCont)
  1494. genArg :: HasDebugCallStack => StgArg -> G [JExpr]
  1495. genArg (StgLitArg l) = genLit l
  1496. genArg a@(StgVarArg i) = do
  1497. unFloat <- use gsUnfloated
  1498. case lookupUFM unFloat i of
  1499. Nothing -> reg
  1500. Just expr -> unfloated expr
  1501. where
  1502. -- if our argument is a joinid, it can be an unboxed tuple
  1503. r :: HasDebugCallStack => VarType
  1504. r = trace' ("r: " ++ showIndent a) r0
  1505. r0 :: HasDebugCallStack => VarType
  1506. r0 = uTypeVt . stgArgType $ a
  1507. reg
  1508. | isVoid r = return []
  1509. | i == trueDataConId = return [ [je| true |] ]
  1510. | i == falseDataConId = return [ [je| false |] ]
  1511. | isMultiVar r = mapM (jsIdN i) [1..varSize r]
  1512. | otherwise = (:[]) <$> jsId i
  1513. unfloated :: HasDebugCallStack => StgExpr -> G [JExpr]
  1514. unfloated (StgLit l) = genLit l
  1515. unfloated (StgConApp dc args _)
  1516. | isBoolTy (dataConType dc) || isUnboxableCon dc =
  1517. (:[]) . allocUnboxedCon dc . concat <$> mapM genArg args
  1518. | null args = (:[]) <$> jsId (dataConWorkId dc)
  1519. | otherwise = do
  1520. as <- concat <$> mapM genArg args
  1521. e <- enterDataCon dc
  1522. cs <- use gsSettings
  1523. return [allocDynamicE cs e as Nothing] -- FIXME: ccs
  1524. unfloated x = panic $ "genArg: unexpected unfloated expression: " ++
  1525. show x
  1526. genStaticArg :: HasDebugCallStack => StgArg -> G [StaticArg]
  1527. genStaticArg (StgLitArg l) = map StaticLitArg <$> genStaticLit l
  1528. genStaticArg a@(StgVarArg i) = do
  1529. unFloat <- use gsUnfloated
  1530. case lookupUFM unFloat i of
  1531. Nothing -> reg
  1532. Just expr -> unfloated expr
  1533. where
  1534. r = uTypeVt . stgArgType $ a
  1535. reg
  1536. | isVoid r =
  1537. return []
  1538. | i == trueDataConId =
  1539. return [StaticLitArg (BoolLit True)]
  1540. | i == falseDataConId =
  1541. return [StaticLitArg (BoolLit False)]
  1542. | isMultiVar r =
  1543. map (\(TxtI t) -> StaticObjArg t) <$> mapM (jsIdIN i) [1..varSize r] -- this seems wrong, not an obj?
  1544. | otherwise = (\(TxtI it) -> [StaticObjArg it]) <$> jsIdI i
  1545. unfloated :: StgExpr -> G [StaticArg]
  1546. unfloated (StgLit l) = map StaticLitArg <$> genStaticLit l
  1547. unfloated (StgConApp dc args _)
  1548. | isBoolTy (dataConType dc) || isUnboxableCon dc =
  1549. (:[]) . allocUnboxedConStatic dc . concat <$> mapM genStaticArg args -- fixme what is allocunboxedcon?
  1550. | null args = (\(TxtI t) -> [StaticObjArg t]) <$> jsIdI (dataConWorkId dc)
  1551. | otherwise = do
  1552. as <- concat <$> mapM genStaticArg args
  1553. (TxtI e) <- enterDataConI dc
  1554. return [StaticConArg e as]
  1555. unfloated x = panic ("genArg: unexpected unfloated expression: " ++ show x)
  1556. allocateStaticList :: [StgArg] -> StgArg -> G StaticVal
  1557. allocateStaticList xs a@(StgVarArg i)
  1558. | isDataConId_maybe i == Just nilDataCon = listAlloc xs Nothing
  1559. | otherwise = do
  1560. unFloat <- use gsUnfloated
  1561. case lookupUFM unFloat i of
  1562. Just (StgConApp dc [h,t] _)
  1563. | dc == consDataCon -> allocateStaticList (h:xs) t
  1564. _ -> listAlloc xs (Just a)
  1565. where
  1566. listAlloc :: [StgArg] -> Maybe StgArg -> G StaticVal
  1567. listAlloc xs Nothing = do
  1568. as <- concat . reverse <$> mapM genStaticArg xs
  1569. return (StaticList as Nothing)
  1570. listAlloc xs (Just r) = do
  1571. as <- concat . reverse <$> mapM genStaticArg xs
  1572. r' <- genStaticArg r
  1573. case r' of
  1574. [StaticObjArg ri] -> return (StaticList as (Just ri))
  1575. _ ->
  1576. panic $ "allocateStaticList: invalid argument (tail): " ++
  1577. show xs ++
  1578. " " ++
  1579. show r
  1580. allocateStaticList _ _ = panic "allocateStaticList: unexpected literal in list"
  1581. -- generate arg to be passed to FFI call, with marshalling JStat to be run
  1582. -- before the call
  1583. genFFIArg :: Bool -> StgArg -> G (JStat, [JExpr])
  1584. genFFIArg isJavaScriptCc (StgLitArg l) = (mempty,) <$> genLit l
  1585. genFFIArg isJavaScriptCc a@(StgVarArg i)
  1586. | not isJavaScriptCc &&
  1587. (tycon == byteArrayPrimTyCon || tycon == mutableByteArrayPrimTyCon) = do
  1588. (\x -> (mempty,[x,jint 0])) <$> jsId i
  1589. | isVoid r = return (mempty, [])
  1590. -- | Just x <- marshalFFIArg a = x
  1591. | isMultiVar r = (mempty,) <$> mapM (jsIdN i) [1..varSize r]
  1592. | otherwise = (\x -> (mempty,[x])) <$> jsId i
  1593. where
  1594. tycon = tyConAppTyCon (unwrapType arg_ty)
  1595. arg_ty = stgArgType a
  1596. r = uTypeVt arg_ty
  1597. genIdArg :: HasDebugCallStack => Id -> G [JExpr]
  1598. genIdArg i = genArg (StgVarArg i)
  1599. genIdArgI :: HasDebugCallStack => Id -> G [Ident]
  1600. genIdArgI i = trace' ("genIdArgI: " ++ show i) (genIdArgI0 i)
  1601. genIdArgI0 :: HasDebugCallStack => Id -> G [Ident]
  1602. genIdArgI0 i
  1603. | isVoid r = return []
  1604. | isMultiVar r = mapM (jsIdIN i) [1..varSize r]
  1605. | otherwise = (:[]) <$> jsIdI i
  1606. where
  1607. r = uTypeVt . idType $ i
  1608. genIdStackArgI :: HasDebugCallStack => Id -> G [(Ident,StackSlot)]
  1609. genIdStackArgI i = zipWith f [1..] <$> genIdArgI i
  1610. where
  1611. f :: Int -> Ident -> (Ident,StackSlot)
  1612. f n ident = (ident, SlotId i n)
  1613. r2d :: Rational -> Double
  1614. r2d = realToFrac
  1615. r2f :: Rational -> Double
  1616. r2f = float2Double . realToFrac
  1617. genStrThunk :: HasDebugCallStack
  1618. => Id
  1619. -> Bool
  1620. -> B.ByteString
  1621. -> CostCentreStack
  1622. -> C
  1623. genStrThunk i nonAscii str cc = do
  1624. ii@(TxtI iit) <- jsIdI i
  1625. let d = decl ii
  1626. ccs <- costCentreStackLbl cc
  1627. let ccsArg = map toJExpr $ maybeToList ccs
  1628. emitStatic iit (StaticThunk Nothing) Nothing
  1629. return $ case decodeModifiedUTF8 str of
  1630. Just t -> d <>
  1631. if nonAscii
  1632. then [j| `ii` = `ApplExpr (jvar "h$strt") $
  1633. [toJExpr $ T.unpack t] ++ ccsArg`; |]
  1634. else [j| `ii` = `ApplExpr (jvar "h$strta") $
  1635. [toJExpr $ T.unpack t] ++ ccsArg`; |]
  1636. Nothing -> d <>
  1637. if nonAscii
  1638. then [j| `ii` = `ApplExpr (jvar "h$strtb") $
  1639. [toJExpr $ map toInteger (B.unpack str)] ++ ccsArg`; |]
  1640. else [j| `ii` = `ApplExpr (jvar "h$strta") $
  1641. [toJExpr $ map (chr.fromIntegral) (B.unpack str)] ++ ccsArg`; |]
  1642. genLit :: HasDebugCallStack => Literal -> G [JExpr]
  1643. genLit (MachChar c) = return [ [je| `ord c` |] ]
  1644. genLit (MachStr str) =
  1645. withNewIdent $ \strLit@(TxtI strLitT) ->
  1646. withNewIdent $ \strOff@(TxtI strOffT) -> do
  1647. emitStatic strLitT (StaticUnboxed (StaticUnboxedString str)) Nothing
  1648. emitStatic strOffT (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing
  1649. return [ ValExpr (JVar strLit), ValExpr (JVar strOff) ]
  1650. genLit MachNullAddr = return [ [je| null |], [je| 0 |] ]
  1651. genLit (LitNumber LitNumInt i _) = return [ [je| `intLit i` |] ]
  1652. genLit (LitNumber LitNumInt64 i _) = return [ [je| `intLit (shiftR i 32)` |]
  1653. , [je| `toSigned i` |]
  1654. ]
  1655. genLit (LitNumber LitNumWord w _) = return [ [je| `toSigned w` |] ]
  1656. genLit (LitNumber LitNumWord64 w _) = return [ [je| `toSigned (shiftR w 32)` |]
  1657. , [je| `toSigned w` |]
  1658. ]
  1659. genLit (MachFloat r) = return [ [je| `r2f r` |] ]
  1660. genLit (MachDouble r) = return [ [je| `r2d r` |] ]
  1661. genLit (MachLabel name _size fod)
  1662. | fod == IsFunction = return [ [je| h$mkFunctionPtr(`TxtI . T.pack $ "h$" ++ unpackFS name`) |], [je| 0 |] ]
  1663. | otherwise = return [ iex (TxtI . T.pack $ "h$" ++ unpackFS name), [je| 0 |] ]
  1664. genLit l = panic $ "genLit: " ++ show l -- unhandled numeric literal" -- removed by CorePrep
  1665. -- | generate a literal for the static init tables
  1666. genStaticLit :: Literal -> G [StaticLit]
  1667. genStaticLit (MachChar c) = return [ IntLit (fromIntegral $ ord c) ]
  1668. genStaticLit (MachStr str) =
  1669. case T.decodeUtf8' str of
  1670. Right t -> return [ StringLit t, IntLit 0 ]
  1671. Left _ -> return [ BinLit str, IntLit 0]
  1672. genStaticLit MachNullAddr = return [ NullLit, IntLit 0 ]
  1673. genStaticLit (LitNumber LitNumInt i _) = return [ IntLit (fromIntegral i) ]
  1674. genStaticLit (LitNumber LitNumInt64 i _) = return [ IntLit (i `shiftR` 32)
  1675. , IntLit (toSigned i)
  1676. ]
  1677. genStaticLit (LitNumber LitNumWord w _) = return [ IntLit (toSigned w) ]
  1678. genStaticLit (LitNumber LitNumWord64 w _) = return [ IntLit (toSigned (w `shiftR` 32))
  1679. , IntLit (toSigned w)
  1680. ]
  1681. genStaticLit (MachFloat r) = return [ DoubleLit . SaneDouble . r2f $ r ]
  1682. genStaticLit (MachDouble r) = return [ DoubleLit . SaneDouble . r2d $ r ]
  1683. genStaticLit (MachLabel name _size fod) =
  1684. return [ LabelLit (fod == IsFunction) (T.pack $ "h$" ++ unpackFS name)
  1685. , IntLit 0
  1686. ]
  1687. genStaticLit l = panic $ "genStaticLit: " ++
  1688. show l
  1689. -- make a signed 32 bit int from this unsigned one, lower 32 bits
  1690. toSigned :: Integer -> Integer
  1691. toSigned i | testBit i 31 = complement (0x7FFFFFFF `xor` (i.&.0x7FFFFFFF))
  1692. | otherwise = i.&.0xFFFFFFFF
  1693. -- truncate literal to fit in 32 bit int
  1694. intLit :: Integer -> Integer
  1695. intLit i = fromIntegral (fromIntegral i :: Int32)
  1696. genSingleLit :: Literal -> G JExpr
  1697. genSingleLit l = do
  1698. es <- genLit l
  1699. case es of
  1700. [e] -> return e
  1701. _ -> panic "genSingleLit: expected single-variable literal"
  1702. genCon :: ExprCtx -> DataCon -> [JExpr] -> C
  1703. genCon tgt con args
  1704. -- fixme should we check the primreps here?
  1705. | isUnboxedTupleCon con && length (concatMap snd $ tgt^.ctxTarget) == length args =
  1706. return $ assignAll (concatMap snd $ tgt ^. ctxTarget) args
  1707. genCon tgt con args | isUnboxedTupleCon con =
  1708. panic ("genCon: unhandled DataCon:\n" ++
  1709. show con ++ "\n" ++
  1710. show (tgt ^. ctxTop) ++ "\n" ++
  1711. show (tgt ^. ctxTarget) ++ "\n" ++
  1712. show args)
  1713. genCon tgt con args | [ValExpr (JVar tgti)] <- concatMap snd (tgt ^. ctxTarget) =
  1714. allocCon tgti con currentCCS args
  1715. genCon tgt con args =
  1716. return mempty -- fixme, do we get missing VecRep things because of this?
  1717. -- panic ("genCon: unhandled DataCon: " ++ show con ++ " " ++ show (tgt ^. ctxTop, length args))
  1718. allocCon :: Ident -> DataCon -> CostCentreStack -> [JExpr] -> C
  1719. allocCon to con cc xs
  1720. | isBoolTy (dataConType con) || isUnboxableCon con = do
  1721. return [j| `to` = `allocUnboxedCon con xs`; |]
  1722. {- | null xs = do
  1723. i <- jsId (dataConWorkId con)
  1724. return (assignj to i) -}
  1725. | otherwise = do
  1726. e <- enterDataCon con
  1727. cs <- use gsSettings
  1728. prof <- profiling
  1729. ccsJ <- if prof then ccsVarJ cc else return Nothing
  1730. return $ allocDynamic cs False to e xs ccsJ
  1731. allocUnboxedCon :: DataCon -> [JExpr] -> JExpr
  1732. allocUnboxedCon con []
  1733. | isBoolTy (dataConType con) && dataConTag con == 1 = [je| false |]
  1734. | isBoolTy (dataConType con) && dataConTag con == 2 = [je| true |]
  1735. allocUnboxedCon con [x]
  1736. | isUnboxableCon con = x
  1737. allocUnboxedCon con xs = panic ("allocUnboxedCon: not an unboxed constructor: " ++ show con ++ " " ++ show xs)
  1738. allocUnboxedConStatic :: DataCon -> [StaticArg] -> StaticArg
  1739. allocUnboxedConStatic con []
  1740. | isBoolTy (dataConType con) && dataConTag con == 1 =
  1741. StaticLitArg (BoolLit False)
  1742. | isBoolTy (dataConType con) && dataConTag con == 2 =
  1743. StaticLitArg (BoolLit True)
  1744. allocUnboxedConStatic _ [a@(StaticLitArg (IntLit _i))] = a
  1745. allocUnboxedConStatic _ [a@(StaticLitArg (DoubleLit _d))] = a
  1746. allocUnboxedConStatic con _ =
  1747. panic ("allocUnboxedConStatic: not an unboxed constructor: " ++ show con)
  1748. allocConStatic :: HasDebugCallStack => Ident -> CostCentreStack -> DataCon -> [GenStgArg Id] {- -> Bool -} -> G ()
  1749. allocConStatic (TxtI to) cc con args -- isRecursive
  1750. {- | trace' ("allocConStatic: " ++ show to ++ " " ++ show con ++ " " ++ show args) True -} = do
  1751. as <- mapM genStaticArg args
  1752. cc' <- costCentreStackLbl cc
  1753. allocConStatic' cc' (concat as)
  1754. where
  1755. allocConStatic' :: HasDebugCallStack => Maybe Ident -> [StaticArg] -> G ()
  1756. allocConStatic' cc' []
  1757. | isBoolTy (dataConType con) && dataConTag con == 1 =
  1758. emitStatic to (StaticUnboxed $ StaticUnboxedBool False) cc'
  1759. | isBoolTy (dataConType con) && dataConTag con == 2 =
  1760. emitStatic to (StaticUnboxed $ StaticUnboxedBool True) cc'
  1761. | otherwise = do
  1762. (TxtI e) <- enterDataConI con
  1763. emitStatic to (StaticData e []) cc'
  1764. allocConStatic' cc' [x]
  1765. | isUnboxableCon con =
  1766. case x of
  1767. StaticLitArg (IntLit i) ->
  1768. emitStatic to (StaticUnboxed $ StaticUnboxedInt i) cc'
  1769. StaticLitArg (BoolLit b) ->
  1770. emitStatic to (StaticUnboxed $ StaticUnboxedBool b) cc'
  1771. StaticLitArg (DoubleLit d) ->
  1772. emitStatic to (StaticUnboxed $ StaticUnboxedDouble d) cc'
  1773. _ ->
  1774. panic $ "allocConStatic: invalid unboxed literal: " ++ show x
  1775. allocConStatic' cc' xs =
  1776. if con == consDataCon
  1777. then flip (emitStatic to) cc' =<< allocateStaticList [args !!! 0] (args !!! 1)
  1778. else do
  1779. (TxtI e) <- enterDataConI con
  1780. emitStatic to (StaticData e xs) cc'
  1781. -- avoid one indirection for global ids
  1782. -- fixme in many cases we can also jump directly to the entry for local?
  1783. jumpToII :: Id -> [JExpr] -> JStat -> C
  1784. jumpToII i args afterLoad
  1785. | isLocalId i = do
  1786. ii <- jsId i
  1787. return (ra <> afterLoad <> [j| return `ii`.f; |])
  1788. | otherwise = do
  1789. ei <- jsEntryId i
  1790. return (ra <> afterLoad <> [j| return `ei`; |])
  1791. where
  1792. ra = mconcat . reverse $ zipWith (\r a -> [j| `r` = `a`; |]) (enumFrom R2) args
  1793. jumpToFast :: HasDebugCallStack => [StgArg] -> JStat -> C
  1794. jumpToFast as afterLoad = do
  1795. regs <- concatMapM genArg as
  1796. (fun, spec) <- selectApply True (as,regs)
  1797. if spec
  1798. then return $ mconcat (ra regs) <> afterLoad <> [j| return `fun`(); |]
  1799. else return $ mconcat (ra regs) <> afterLoad <> [j| return `fun`(`mkTag regs as`); |]
  1800. where
  1801. ra regs = reverse $ zipWith (\r e -> [j| `r` = `e` |]) (enumFrom R2) regs
  1802. mkTag rs as = (length rs `shiftL` 8) .|. length as
  1803. -- find a specialized application path if there is one
  1804. selectApply :: Bool -- ^ true for fast apply, false for stack apply
  1805. -> ([StgArg], [JExpr]) -- ^ arguments
  1806. -> G (JExpr,Bool) -- ^ the function to call, true if specialized path
  1807. selectApply fast (args, as) = do
  1808. case specApply fast (length args) (length as) of
  1809. Just e -> return (e, True)
  1810. Nothing -> return (jsv $ "h$ap_gen" <> fastSuff, False)
  1811. where
  1812. fastSuff | fast = "_fast"
  1813. | otherwise = ""
  1814. -- fixme: what if the call returns a thunk?
  1815. genPrimCall :: ExprCtx -> PrimCall -> [StgArg] -> Type -> G (JStat, ExprResult)
  1816. genPrimCall top (PrimCall lbl _) args t = do
  1817. j <- parseFFIPattern False False False ("h$" ++ unpackFS lbl) t (map toJExpr . concatMap snd $ top ^. ctxTarget) args
  1818. return (j, ExprInline Nothing)
  1819. getObjectKeyValuePairs :: [StgArg] -> Maybe [(Text, StgArg)]
  1820. getObjectKeyValuePairs [] = Just []
  1821. getObjectKeyValuePairs (k:v:xs)
  1822. | Just t <- argJSStringLitUnfolding k =
  1823. fmap ((t,v):) (getObjectKeyValuePairs xs)
  1824. getObjectKeyValuePairs _ = Nothing
  1825. argJSStringLitUnfolding :: StgArg -> Maybe Text
  1826. argJSStringLitUnfolding (StgVarArg v)
  1827. | False = Just "abc" -- fixme
  1828. argJSStringLitUnfolding _ = Nothing
  1829. genForeignCall :: HasDebugCallStack
  1830. => ExprCtx
  1831. -> ForeignCall
  1832. -> Type
  1833. -> [JExpr]
  1834. -> [StgArg]
  1835. -> G (JStat, ExprResult)
  1836. genForeignCall top
  1837. (CCall (CCallSpec (StaticTarget _ tgt Nothing True)
  1838. JavaScriptCallConv
  1839. PlayRisky))
  1840. t
  1841. [obj]
  1842. args
  1843. | tgt == fsLit "h$buildObject"
  1844. , Just pairs <- getObjectKeyValuePairs args = do
  1845. pairs' <- mapM (\(k,v) -> genArg v >>= \([v']) -> return (k,v')) pairs
  1846. return ( assignj obj (ValExpr (JHash $ M.fromList pairs'))
  1847. , ExprInline Nothing
  1848. )
  1849. genForeignCall top (CCall (CCallSpec ccTarget cconv safety)) t tgt args = do
  1850. emitForeign (top ^. ctxSrcSpan) (T.pack lbl) safety cconv (map showArgType args) (showType t)
  1851. (,exprResult) <$> parseFFIPattern catchExcep async isJsCc lbl t tgt' args
  1852. where
  1853. isJsCc = cconv == JavaScriptCallConv
  1854. lbl | (StaticTarget _ clbl _mpkg _isFunPtr) <- ccTarget
  1855. = let clbl' = unpackFS clbl
  1856. in if | isJsCc -> clbl'
  1857. | wrapperPrefix `L.isPrefixOf` clbl' ->
  1858. ("h$" ++ (drop 2 $ dropWhile isDigit $ drop (length wrapperPrefix) clbl'))
  1859. | otherwise -> "h$" ++ clbl'
  1860. | otherwise = "h$callDynamic"
  1861. exprResult | async = ExprCont
  1862. | otherwise = ExprInline Nothing
  1863. catchExcep = (cconv == JavaScriptCallConv) &&
  1864. playSafe safety || playInterruptible safety
  1865. async | isJsCc = playInterruptible safety
  1866. | otherwise = playInterruptible safety || playSafe safety
  1867. tgt' | async = take (length tgt) (map toJExpr $ enumFrom R1)
  1868. | otherwise = tgt
  1869. wrapperPrefix = "ghczuwrapperZC"
  1870. -- | generate the actual call
  1871. {-
  1872. parse FFI patterns:
  1873. "&value -> value
  1874. 1. "function" -> ret = function(...)
  1875. 2. "$r = $1.f($2) -> r1 = a1.f(a2)
  1876. arguments, $1, $2, $3 unary arguments
  1877. $1_1, $1_2, for a binary argument
  1878. return type examples
  1879. 1. $r unary return
  1880. 2. $r1, $r2 binary return
  1881. 3. $r1, $r2, $r3_1, $r3_2 unboxed tuple return
  1882. -}
  1883. parseFFIPattern :: Bool -- ^ catch exception and convert them to haskell exceptions
  1884. -> Bool -- ^ async (only valid with javascript calling conv)
  1885. -> Bool -- ^ using javascript calling convention
  1886. -> String
  1887. -> Type
  1888. -> [JExpr]
  1889. -> [StgArg]
  1890. -> C
  1891. parseFFIPattern catchExcep async jscc pat t es as
  1892. | catchExcep = do
  1893. c <- parseFFIPatternA async jscc pat t es as
  1894. return [j| try {
  1895. `c`;
  1896. } catch(e) {
  1897. return h$throwJSException(e);
  1898. }
  1899. |]
  1900. | otherwise = parseFFIPatternA async jscc pat t es as
  1901. parseFFIPatternA :: Bool -- ^ async
  1902. -> Bool -- ^ using JavaScript calling conv
  1903. -> String
  1904. -> Type
  1905. -> [JExpr]
  1906. -> [StgArg]
  1907. -> C
  1908. -- async calls get an extra callback argument
  1909. -- call it with the result
  1910. parseFFIPatternA True True pat t es as = do
  1911. cb <- makeIdent
  1912. stat <- parseFFIPattern' (Just (toJExpr cb)) True pat t es as
  1913. return [j| `decl cb`;
  1914. var x = { mv: null };
  1915. `cb` = h$mkForeignCallback(x);
  1916. `stat`;
  1917. if(x.mv === null) {
  1918. x.mv = new h$MVar();
  1919. `Sp` = `Sp` + 1;
  1920. `Stack`[`Sp`] = h$unboxFFIResult;
  1921. return h$takeMVar(x.mv);
  1922. } else {
  1923. var d = x.mv;
  1924. `copyResult d`;
  1925. }
  1926. |]
  1927. where nrst = typeSize t
  1928. copyResult d = assignAll es (map (\i -> [je| `d`[`i`] |]) [0..nrst-1])
  1929. parseFFIPatternA _async javascriptCc pat t es as =
  1930. parseFFIPattern' Nothing javascriptCc pat t es as
  1931. -- parseFFIPatternA _ _ _ _ _ _ = error "parseFFIPattern: non-JavaScript pattern must be synchronous"
  1932. parseFFIPattern' :: Maybe JExpr -- ^ Nothing for sync, Just callback for async
  1933. -> Bool -- ^ javascript calling convention used
  1934. -> String -- ^ pattern called
  1935. -> Type -- ^ return type
  1936. -> [JExpr] -- ^ expressions to return in (may be more than necessary)
  1937. -> [StgArg] -- ^ arguments
  1938. -> C
  1939. parseFFIPattern' callback javascriptCc pat t ret args
  1940. | not javascriptCc = mkApply pat
  1941. | otherwise = do
  1942. u <- freshUnique
  1943. case parseFfiJME pat u of
  1944. Right (ValExpr (JVar (TxtI _ident))) -> mkApply pat
  1945. Right expr | not async && length tgt < 2 -> do
  1946. (statPre, ap) <- argPlaceholders javascriptCc args
  1947. let rp = resultPlaceholders async t ret
  1948. env = M.fromList (rp ++ ap)
  1949. if length tgt == 1
  1950. then return $ statPre <> (everywhere (mkT $ replaceIdent env) [j| $r = `expr`; |])
  1951. else return $ statPre <> (everywhere (mkT $ replaceIdent env) (toStat expr))
  1952. Right _ -> p $ "invalid expression FFI pattern. Expression FFI patterns can only be used for synchronous FFI " ++
  1953. " imports with result size 0 or 1.\n" ++ pat
  1954. Left _ -> case parseFfiJM pat u of
  1955. Left err -> p (show err)
  1956. Right stat -> do
  1957. let rp = resultPlaceholders async t ret
  1958. let cp = callbackPlaceholders callback
  1959. (statPre, ap) <- argPlaceholders javascriptCc args
  1960. let env = M.fromList (rp ++ ap ++ cp)
  1961. return $ statPre <> (everywhere (mkT $ replaceIdent env) stat) -- fixme trace?
  1962. where
  1963. async = isJust callback
  1964. tgt = take (typeSize t) ret
  1965. -- automatic apply, build call and result copy
  1966. mkApply f
  1967. | Just cb <- callback = do
  1968. (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
  1969. cs <- use gsSettings
  1970. return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as++[cb])
  1971. | (ts@(_:_)) <- tgt = do
  1972. (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
  1973. case ts of
  1974. (t:ts') -> do
  1975. cs <- use gsSettings
  1976. return $ traceCall cs as
  1977. <> mconcat stats
  1978. <> [j| `t` = `ApplExpr f' (concat as)`; |]
  1979. <> copyResult ts'
  1980. <> mempty
  1981. _ -> error "mkApply: empty list"
  1982. | otherwise = do
  1983. (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args
  1984. cs <- use gsSettings
  1985. return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as)
  1986. where f' = toJExpr (TxtI $ T.pack f)
  1987. copyResult rs = mconcat $ zipWith (\t r -> [j| `r`=`t`;|]) (enumFrom Ret1) rs
  1988. p e = error ("Parse error in FFI pattern: " ++ pat ++ "\n" ++ e)
  1989. replaceIdent :: Map Ident JExpr -> JExpr -> JExpr
  1990. replaceIdent env e@(ValExpr (JVar i))
  1991. | isFFIPlaceholder i = fromMaybe err (M.lookup i env)
  1992. | otherwise = e
  1993. where
  1994. (TxtI i') = i
  1995. err = error (pat ++ ": invalid placeholder, check function type: " ++ show (i', args, t))
  1996. replaceIdent _ e = e
  1997. traceCall cs as
  1998. | csTraceForeign cs = [j| h$traceForeign(`pat`, `as`); |]
  1999. | otherwise = mempty
  2000. showArgType :: StgArg -> Text
  2001. showArgType a = showType (stgArgType a)
  2002. showType :: Type -> Text
  2003. showType t
  2004. | Just tc <- tyConAppTyCon_maybe (unwrapType t) =
  2005. T.pack (show tc)
  2006. | otherwise = "<unknown>"
  2007. -- parse and saturate ffi splice
  2008. parseFfiJME :: String -> Int -> Either P.ParseError JExpr
  2009. parseFfiJME xs u = fmap (saturateFFI u) . parseJME $ xs
  2010. -- parse and saturate ffi splice, check for unhygienic declarations
  2011. parseFfiJM :: String -> Int -> Either P.ParseError JStat
  2012. parseFfiJM xs u = fmap (makeHygienic . saturateFFI u) . parseJM $ xs
  2013. where
  2014. makeHygienic :: JStat -> JStat
  2015. makeHygienic s = snd $ O.renameLocalsFun (map addFFIToken newLocals) ([], s)
  2016. -- addFFIToken (StrI xs) = TxtI (T.pack $ "ghcjs_ffi_" ++ show u ++ "_" ++ xs)
  2017. addFFIToken (TxtI xs) = TxtI (T.pack ("ghcjs_ffi_" ++ show u ++ "_") <> xs)
  2018. saturateFFI :: JMacro a => Int -> a -> a
  2019. saturateFFI u = jsSaturate (Just . T.pack $ "ghcjs_ffi_sat_" ++ show u)
  2020. -- $r for single, $r1,$r2 for dual
  2021. -- $r1, $r2, etc for ubx tup, void args not counted
  2022. resultPlaceholders :: Bool -> Type -> [JExpr] -> [(Ident,JExpr)] -- ident, replacement
  2023. resultPlaceholders True _ _ = [] -- async has no direct resuls, use callback
  2024. resultPlaceholders False t rs =
  2025. case typeVt (unwrapType t) of
  2026. [t'] -> mkUnary (varSize t')
  2027. uts ->
  2028. let sizes = filter (>0) (map varSize uts)
  2029. f _ 0 = []
  2030. f n 1 = [["$r" ++ show n]]
  2031. f n k = ["$r" ++ sn, "$r" ++ sn ++ "_1"] : map (\x -> ["$r" ++ sn ++ "_" ++ show x]) [2..k]
  2032. where sn = show n
  2033. phs = zipWith (\size n -> f n size) sizes [(1::Int)..]
  2034. in case sizes of
  2035. [n] -> mkUnary n
  2036. _ -> concat $ zipWith (\phs' r -> map (\i -> (TxtI (T.pack i), r)) phs') (concat phs) rs
  2037. where
  2038. mkUnary 0 = []
  2039. mkUnary 1 = [(TxtI "$r",head rs)] -- single
  2040. mkUnary n = [(TxtI "$r",head rs),(TxtI "$r1", head rs)] ++
  2041. zipWith (\n r -> (TxtI . T.pack $ "$r" ++ show n, toJExpr r)) [2..n] (tail rs)
  2042. -- $1, $2, $3 for single, $1_1, $1_2 etc for dual
  2043. -- void args not counted
  2044. argPlaceholders :: Bool -> [StgArg] -> G (JStat, [(Ident,JExpr)])
  2045. argPlaceholders isJavaScriptCc args = do
  2046. (stats, idents0) <- unzip <$> mapM (genFFIArg isJavaScriptCc) args
  2047. let idents = filter (not . null) idents0
  2048. return $ (mconcat stats, concat
  2049. (zipWith (\is n -> mkPlaceholder True ("$"++show n) is) idents [(1::Int)..]))
  2050. callbackPlaceholders :: Maybe JExpr -> [(Ident,JExpr)]
  2051. callbackPlaceholders Nothing = []
  2052. callbackPlaceholders (Just e) = [((TxtI "$c"), e)]
  2053. mkPlaceholder :: Bool -> String -> [JExpr] -> [(Ident, JExpr)]
  2054. mkPlaceholder undersc prefix aids =
  2055. case aids of
  2056. [] -> []
  2057. [x] -> [(TxtI . T.pack $ prefix, x)]
  2058. xs@(x:_) -> (TxtI . T.pack $ prefix, x) :
  2059. zipWith (\x m -> (TxtI . T.pack $ prefix ++ u ++ show m,x)) xs [(1::Int)..]
  2060. where u = if undersc then "_" else ""
  2061. -- ident is $N, $N_R, $rN, $rN_R or $r or $c
  2062. isFFIPlaceholder :: Ident -> Bool
  2063. isFFIPlaceholder (TxtI x) =
  2064. either (const False) (const True) (P.parse parser "" x)
  2065. where
  2066. parser = void (P.try $ P.string "$r") <|>
  2067. void (P.try $ P.string "$c") <|> do
  2068. P.char '$'
  2069. P.optional (P.char 'r')
  2070. P.many1 P.digit
  2071. P.optional (P.char '_' >> P.many1 P.digit)
  2072. withNewIdent :: (Ident -> G a) -> G a
  2073. withNewIdent m = makeIdent >>= m
  2074. makeIdent :: G Ident
  2075. makeIdent = do
  2076. gsId += 1
  2077. i <- use gsId
  2078. mod <- use gsModule
  2079. return (TxtI . T.pack $ "h$$" ++
  2080. zEncodeString (show mod) ++
  2081. "_" ++
  2082. encodeUnique i
  2083. )
  2084. freshUnique :: G Int
  2085. freshUnique = gsId += 1 >> use gsId
  2086. -- returns True if the expression is definitely inline
  2087. isInlineExpr :: UniqSet Id -> StgExpr -> (UniqSet Id, Bool)
  2088. isInlineExpr v (StgApp i args) =
  2089. (emptyUniqSet, isInlineApp v i args)
  2090. isInlineExpr _ (StgLit{}) =
  2091. (emptyUniqSet, True)
  2092. isInlineExpr _ (StgConApp{}) =
  2093. (emptyUniqSet, True)
  2094. isInlineExpr _ (StgOpApp (StgFCallOp f _) _ _) =
  2095. (emptyUniqSet, isInlineForeignCall f)
  2096. isInlineExpr v (StgOpApp (StgPrimOp SeqOp) [StgVarArg e] t) =
  2097. (emptyUniqSet, e `elementOfUniqSet` v || isStrictType t)
  2098. isInlineExpr _ (StgOpApp (StgPrimOp op) _ _) =
  2099. (emptyUniqSet, isInlinePrimOp op)
  2100. isInlineExpr _ (StgOpApp (StgPrimCallOp _c) _ _) =
  2101. (emptyUniqSet, True)
  2102. isInlineExpr _ (StgLam{}) =
  2103. (emptyUniqSet, True)
  2104. isInlineExpr v (StgCase e b _ alts) =
  2105. let (_ve, ie) = isInlineExpr v e
  2106. v' = addOneToUniqSet v b
  2107. (vas, ias) = unzip $ map (isInlineExpr v') (alts ^.. traverse . _3)
  2108. vr = foldl1' intersectUniqSets vas
  2109. in (vr, (ie || b `elementOfUniqSet` v) && and ias)
  2110. isInlineExpr v (StgLet b e) =
  2111. isInlineExpr (inspectInlineBinding v b) e
  2112. isInlineExpr v (StgLetNoEscape b e) =
  2113. isInlineExpr v e
  2114. isInlineExpr v (StgTick _ e) =
  2115. isInlineExpr v e
  2116. inspectInlineBinding :: UniqSet Id -> StgBinding -> UniqSet Id
  2117. inspectInlineBinding v (StgNonRec i r) = inspectInlineRhs v i r
  2118. inspectInlineBinding v (StgRec bs) =
  2119. foldl' (\v' (i,r) -> inspectInlineRhs v' i r) v bs
  2120. inspectInlineRhs :: UniqSet Id -> Id -> StgRhs -> UniqSet Id
  2121. inspectInlineRhs v i (StgRhsCon{}) = addOneToUniqSet v i
  2122. inspectInlineRhs v i (StgRhsClosure _ _ _ ReEntrant _ _) = addOneToUniqSet v i
  2123. inspectInlineRhs v _ _ = v
  2124. isInlineForeignCall :: ForeignCall -> Bool
  2125. isInlineForeignCall (CCall (CCallSpec _ cconv safety)) =
  2126. not (playInterruptible safety) &&
  2127. not (cconv /= JavaScriptCallConv && playSafe safety)
  2128. isInlineApp :: UniqSet Id -> Id -> [StgArg] -> Bool
  2129. isInlineApp _ i _
  2130. | isJoinId i = False
  2131. isInlineApp v i [] = isUnboxedTupleType (idType i) ||
  2132. isStrictType (idType i) ||
  2133. i `elementOfUniqSet` v ||
  2134. isStrictId i
  2135. isInlineApp v i [StgVarArg a]
  2136. | DataConWrapId dc <- idDetails i
  2137. , isNewTyCon (dataConTyCon dc)
  2138. , isStrictType (idType a) || a `elementOfUniqSet` v || isStrictId a = True
  2139. isInlineApp _ _ _ = False
  2140. verifyMatchRep :: HasDebugCallStack => Id -> AltType -> C
  2141. #ifndef RUNTIME_ASSERTIONS
  2142. verifyMatchRep _ _ = pure mempty
  2143. #else
  2144. verifyMatchRep x (AlgAlt tc) = do
  2145. ix <- genIds x
  2146. pure $ ApplStat (ValExpr (JVar (TxtI "h$verify_match_alg")))
  2147. (ValExpr(JStr(T.pack (show tc))):ix)
  2148. verifyMatchRep _ _ = pure mempty
  2149. #endif
  2150. verifyRuntimeReps :: HasDebugCallStack => [Id] -> C
  2151. #ifndef RUNTIME_ASSERTIONS
  2152. verifyRuntimeReps _ = pure mempty
  2153. #else
  2154. verifyRuntimeReps xs = mconcat <$> mapM verifyRuntimeRep xs
  2155. where
  2156. verifyRuntimeRep i = do
  2157. i' <- genIds i
  2158. pure $ go i' (idVt i)
  2159. go js (VoidV:vs) = go js vs
  2160. go (j1:j2:js) (LongV:vs) = v "h$verify_rep_long" [j1,j2] <> go js vs
  2161. go (j1:j2:js) (AddrV:vs) = v "h$verify_rep_addr" [j1,j2] <> go js vs
  2162. go (j:js) (v:vs) = ver j v <> go js vs
  2163. go [] [] = mempty
  2164. go _ _ = panic
  2165. ("verifyRuntimeReps: inconsistent sizes: " ++ show xs)
  2166. ver j PtrV = v "h$verify_rep_heapobj" [j]
  2167. ver j IntV = v "h$verify_rep_int" [j]
  2168. ver j RtsObjV = v "h$verify_rep_rtsobj" [j]
  2169. ver j DoubleV = v "h$verify_rep_double" [j]
  2170. ver j ArrV = v "h$verify_rep_arr" [j]
  2171. ver _ _ = mempty
  2172. v f as = ApplStat (ValExpr (JVar (TxtI f))) as
  2173. #endif