{-# LANGUAGE LambdaCase #-} module GHC.StgToJS.StgUtils ( bindingRefs , hasExport , collectTopIds , collectIds , removeTick , isUpdatableRhs , isInlineExpr , exprRefs -- * Live vars , LiveVars , liveVars , liveStatic , stgRhsLive , stgExprLive , stgTopBindLive , stgLetNoEscapeLive , stgLneLiveExpr , stgLneLive , stgLneLive' ) where import GHC.Prelude import GHC.Stg.Syntax import GHC.Core.DataCon import GHC.Core.Type import GHC.Core.TyCon import GHC.Types.Unique.FM import GHC.Types.Unique.Set import GHC.Types.Unique import GHC.Types.Id import GHC.Types.Id.Info import GHC.Types.ForeignCall import GHC.Types.TyThing import GHC.Types.Name import GHC.Types.Var.Set import GHC.Builtin.Names import GHC.Builtin.PrimOps (PrimOp(SeqOp), primOpIsReallyInline) import GHC.Utils.Misc (seqList) import GHC.Utils.Panic import qualified Data.Foldable as F import qualified Data.Set as S import qualified Data.List as L import Data.Set (Set) import Data.Monoid s :: a -> Set a s = S.singleton l :: (a -> Set Id) -> [a] -> Set Id l = F.foldMap -- | collect Ids that this binding refers to -- (does not include the bindees themselves) -- first argument is Id -> StgExpr map for unfloated arguments bindingRefs :: UniqFM Id CgStgExpr -> CgStgBinding -> Set Id bindingRefs u = \case StgNonRec _ rhs -> rhsRefs u rhs StgRec bs -> l (rhsRefs u . snd) bs rhsRefs :: UniqFM Id CgStgExpr -> CgStgRhs -> Set Id rhsRefs u = \case StgRhsClosure _ _ _ _ body -> exprRefs u body StgRhsCon _ccs d _mu _ticks args -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args exprRefs :: UniqFM Id CgStgExpr -> CgStgExpr -> Set Id exprRefs u = \case StgApp f args -> s f <> l (argRefs u) args StgConApp d _n args _ -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args StgOpApp _ args _ -> l (argRefs u) args StgLit {} -> mempty StgCase expr _ _ alts -> exprRefs u expr <> mconcat (fmap (altRefs u) alts) StgLet _ bnd expr -> bindingRefs u bnd <> exprRefs u expr StgLetNoEscape _ bnd expr -> bindingRefs u bnd <> exprRefs u expr StgTick _ expr -> exprRefs u expr altRefs :: UniqFM Id CgStgExpr -> CgStgAlt -> Set Id altRefs u alt = exprRefs u (alt_rhs alt) argRefs :: UniqFM Id CgStgExpr -> StgArg -> Set Id argRefs u = \case StgVarArg id | Just e <- lookupUFM u id -> exprRefs u e | otherwise -> s id _ -> mempty hasExport :: CgStgBinding -> Bool hasExport bnd = case bnd of StgNonRec b e -> isExportedBind b e StgRec bs -> any (uncurry isExportedBind) bs where isExportedBind _i (StgRhsCon _cc con _ _ _) = getUnique con == staticPtrDataConKey isExportedBind _ _ = False collectTopIds :: CgStgBinding -> [Id] collectTopIds (StgNonRec b _) = [b] collectTopIds (StgRec bs) = let xs = map (zapFragileIdInfo . fst) bs in seqList xs `seq` xs collectIds :: UniqFM Id CgStgExpr -> CgStgBinding -> [Id] collectIds unfloated b = let xs = map zapFragileIdInfo . filter acceptId $ S.toList (bindingRefs unfloated b) in seqList xs `seq` xs where acceptId i = all ($ i) [not . isForbidden] -- fixme test this: [isExported[isGlobalId, not.isForbidden] -- the GHC.Prim module has no js source file isForbidden i | Just m <- nameModule_maybe (getName i) = m == gHC_PRIM | otherwise = False removeTick :: CgStgExpr -> CgStgExpr removeTick (StgTick _ e) = e removeTick e = e ----------------------------------------------------- -- Live vars -- -- TODO: should probably be moved into GHC.Stg.LiveVars type LiveVars = DVarSet liveStatic :: LiveVars -> LiveVars liveStatic = filterDVarSet isGlobalId liveVars :: LiveVars -> LiveVars liveVars = filterDVarSet (not . isGlobalId) stgTopBindLive :: CgStgTopBinding -> [(Id, LiveVars)] stgTopBindLive = \case StgTopLifted b -> stgBindLive b StgTopStringLit {} -> [] stgBindLive :: CgStgBinding -> [(Id, LiveVars)] stgBindLive = \case StgNonRec b rhs -> [(b, stgRhsLive rhs)] StgRec bs -> map (\(b,rhs) -> (b, stgRhsLive rhs)) bs stgBindRhsLive :: CgStgBinding -> LiveVars stgBindRhsLive b = let (bs, ls) = unzip (stgBindLive b) in delDVarSetList (unionDVarSets ls) bs stgRhsLive :: CgStgRhs -> LiveVars stgRhsLive = \case StgRhsClosure _ _ _ args e -> delDVarSetList (stgExprLive True e) args StgRhsCon _ _ _ _ args -> unionDVarSets (map stgArgLive args) stgArgLive :: StgArg -> LiveVars stgArgLive = \case StgVarArg occ -> unitDVarSet occ StgLitArg {} -> emptyDVarSet stgExprLive :: Bool -> CgStgExpr -> LiveVars stgExprLive includeLHS = \case StgApp occ args -> unionDVarSets (unitDVarSet occ : map stgArgLive args) StgLit {} -> emptyDVarSet StgConApp _dc _n args _tys -> unionDVarSets (map stgArgLive args) StgOpApp _op args _ty -> unionDVarSets (map stgArgLive args) StgCase e b _at alts | includeLHS -> el `unionDVarSet` delDVarSet al b | otherwise -> delDVarSet al b where al = unionDVarSets (map stgAltLive alts) el = stgExprLive True e StgLet _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b) StgLetNoEscape _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b) StgTick _ti e -> stgExprLive True e stgAltLive :: CgStgAlt -> LiveVars stgAltLive alt = delDVarSetList (stgExprLive True (alt_rhs alt)) (alt_bndrs alt) stgLetNoEscapeLive :: Bool -> StgBinding -> StgExpr -> LiveVars stgLetNoEscapeLive _someBool _b _e = panic "stgLetNoEscapeLive" bindees :: CgStgBinding -> [Id] bindees = \case StgNonRec b _e -> [b] StgRec bs -> map fst bs isUpdatableRhs :: CgStgRhs -> Bool isUpdatableRhs (StgRhsClosure _ _ u _ _) = isUpdatable u isUpdatableRhs _ = False stgLneLive' :: CgStgBinding -> [Id] stgLneLive' b = filter (`notElem` bindees b) (stgLneLive b) stgLneLive :: CgStgBinding -> [Id] stgLneLive (StgNonRec _b e) = stgLneLiveExpr e stgLneLive (StgRec bs) = L.nub $ concatMap (stgLneLiveExpr . snd) bs stgLneLiveExpr :: CgStgRhs -> [Id] stgLneLiveExpr rhs = dVarSetElems (liveVars $ stgRhsLive rhs) -- stgLneLiveExpr (StgRhsClosure _ _ _ _ e) = dVarSetElems (liveVars (stgExprLive e)) -- stgLneLiveExpr StgRhsCon {} = [] -- | returns True if the expression is definitely inline isInlineExpr :: UniqSet Id -> CgStgExpr -> (UniqSet Id, Bool) isInlineExpr v = \case StgApp i args -> (emptyUniqSet, isInlineApp v i args) StgLit{} -> (emptyUniqSet, True) StgConApp{} -> (emptyUniqSet, True) StgOpApp (StgFCallOp f _) _ _ -> (emptyUniqSet, isInlineForeignCall f) StgOpApp (StgPrimOp SeqOp) [StgVarArg e] t -> (emptyUniqSet, e `elementOfUniqSet` v || isStrictType t) StgOpApp (StgPrimOp op) _ _ -> (emptyUniqSet, primOpIsReallyInline op) StgOpApp (StgPrimCallOp _c) _ _ -> (emptyUniqSet, True) StgCase e b _ alts ->let (_ve, ie) = isInlineExpr v e v' = addOneToUniqSet v b (vas, ias) = unzip $ map (isInlineExpr v') (fmap alt_rhs alts) vr = L.foldl1' intersectUniqSets vas in (vr, (ie || b `elementOfUniqSet` v) && and ias) StgLet _ b e -> isInlineExpr (inspectInlineBinding v b) e StgLetNoEscape _ _b e -> isInlineExpr v e StgTick _ e -> isInlineExpr v e inspectInlineBinding :: UniqSet Id -> CgStgBinding -> UniqSet Id inspectInlineBinding v = \case StgNonRec i r -> inspectInlineRhs v i r StgRec bs -> foldl' (\v' (i,r) -> inspectInlineRhs v' i r) v bs inspectInlineRhs :: UniqSet Id -> Id -> CgStgRhs -> UniqSet Id inspectInlineRhs v i = \case StgRhsCon{} -> addOneToUniqSet v i StgRhsClosure _ _ ReEntrant _ _ -> addOneToUniqSet v i _ -> v isInlineForeignCall :: ForeignCall -> Bool isInlineForeignCall (CCall (CCallSpec _ cconv safety)) = not (playInterruptible safety) && not (cconv /= JavaScriptCallConv && playSafe safety) isInlineApp :: UniqSet Id -> Id -> [StgArg] -> Bool isInlineApp v i = \case _ | isJoinId i -> False [] -> isUnboxedTupleType (idType i) || isStrictType (idType i) || i `elementOfUniqSet` v [StgVarArg a] | DataConWrapId dc <- idDetails i , isNewTyCon (dataConTyCon dc) , isStrictType (idType a) || a `elementOfUniqSet` v || isStrictId a -> True _ -> False