diff options
author | Kavon Farvardin <kavon@farvard.in> | 2018-09-23 15:29:37 -0500 |
---|---|---|
committer | Kavon Farvardin <kavon@farvard.in> | 2018-09-23 15:29:37 -0500 |
commit | 84c2ad99582391005b5e873198b15e9e9eb4f78d (patch) | |
tree | caa8c2f2ec7e97fbb4977263c6817c9af5025cf4 /compiler/stgSyn | |
parent | 8ddb47cfcf5776e9a3c55fd37947c8a95e00fa12 (diff) | |
parent | e68b439fe5de61b9a2ca51af472185c62ccb8b46 (diff) | |
download | haskell-wip/T13904.tar.gz |
update to current master againwip/T13904
Diffstat (limited to 'compiler/stgSyn')
-rw-r--r-- | compiler/stgSyn/CoreToStg.hs | 257 | ||||
-rw-r--r-- | compiler/stgSyn/StgLint.hs | 526 | ||||
-rw-r--r-- | compiler/stgSyn/StgSyn.hs | 39 |
3 files changed, 373 insertions, 449 deletions
diff --git a/compiler/stgSyn/CoreToStg.hs b/compiler/stgSyn/CoreToStg.hs index 900d23f7b5..fdd8d5bef3 100644 --- a/compiler/stgSyn/CoreToStg.hs +++ b/compiler/stgSyn/CoreToStg.hs @@ -11,12 +11,15 @@ -- And, as we have the info in hand, we may convert some lets to -- let-no-escapes. -module CoreToStg ( coreToStg, coreExprToStg ) where +module CoreToStg ( coreToStg ) where #include "HsVersions.h" +import GhcPrelude + import CoreSyn -import CoreUtils ( exprType, findDefault, isJoinBind ) +import CoreUtils ( exprType, findDefault, isJoinBind + , exprIsTickedString_maybe ) import CoreArity ( manifestArity ) import StgSyn @@ -27,10 +30,10 @@ import MkId ( coercionTokenId ) import Id import IdInfo import DataCon -import CostCentre ( noCCS ) +import CostCentre import VarEnv import Module -import Name ( isExternalName, nameOccName ) +import Name ( isExternalName, nameOccName, nameModule_maybe ) import OccName ( occNameFS ) import BasicTypes ( Arity ) import TysWiredIn ( unboxedUnitDataCon ) @@ -44,7 +47,9 @@ import ForeignCall import Demand ( isUsedOnce ) import PrimOp ( PrimCall(..) ) import UniqFM +import SrcLoc ( mkGeneralSrcSpan ) +import Data.List.NonEmpty (nonEmpty, toList) import Data.Maybe (isJust, fromMaybe) import Control.Monad (liftM, ap) @@ -126,15 +131,6 @@ import Control.Monad (liftM, ap) -- -- The CafInfo has already been calculated during the CoreTidy pass. -- --- During CoreToStg, we then pin onto each binding and case expression, a --- list of Ids which represents the "live" CAFs at that point. The meaning --- of "live" here is the same as for live variables, see above (which is --- why it's convenient to collect CAF information here rather than elsewhere). --- --- The later SRT pass takes these lists of Ids and uses them to construct --- the actual nested SRTs, and replaces the lists of Ids with (offset,length) --- pairs. - -- Note [What is a non-escaping let] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- @@ -194,61 +190,99 @@ import Control.Monad (liftM, ap) -- in -- ...(x b)... +-- Note [Cost-centre initialization plan] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- +-- Previously `coreToStg` was initializing cost-centre stack fields as `noCCS`, +-- and the fields were then fixed by a seperate pass `stgMassageForProfiling`. +-- We now initialize these correctly. The initialization works like this: +-- +-- - For non-top level bindings always use `currentCCS`. +-- +-- - For top-level bindings, check if the binding is a CAF +-- +-- - CAF: If -fcaf-all is enabled, create a new CAF just for this CAF +-- and use it. Note that these new cost centres need to be +-- collected to be able to generate cost centre initialization +-- code, so `coreToTopStgRhs` now returns `CollectedCCs`. +-- +-- If -fcaf-all is not enabled, use "all CAFs" cost centre. +-- +-- - Non-CAF: Top-level (static) data is not counted in heap profiles; nor +-- do we set CCCS from it; so we just slam in +-- dontCareCostCentre. + -- -------------------------------------------------------------- -- Setting variable info: top-level, binds, RHSs -- -------------------------------------------------------------- -coreToStg :: DynFlags -> Module -> CoreProgram -> [StgTopBinding] +coreToStg :: DynFlags -> Module -> CoreProgram + -> ([StgTopBinding], CollectedCCs) coreToStg dflags this_mod pgm - = pgm' - where (_, _, pgm') = coreTopBindsToStg dflags this_mod emptyVarEnv pgm + = (pgm', final_ccs) + where + (_, _, (local_ccs, local_cc_stacks), pgm') + = coreTopBindsToStg dflags this_mod emptyVarEnv emptyCollectedCCs pgm + + prof = WayProf `elem` ways dflags -coreExprToStg :: CoreExpr -> StgExpr -coreExprToStg expr - = new_expr where (new_expr,_) = initCts emptyVarEnv (coreToStgExpr expr) + final_ccs + | prof && gopt Opt_AutoSccsOnIndividualCafs dflags + = (local_ccs,local_cc_stacks) -- don't need "all CAFs" CC + | prof + = (all_cafs_cc:local_ccs, all_cafs_ccs:local_cc_stacks) + | otherwise + = emptyCollectedCCs + (all_cafs_cc, all_cafs_ccs) = getAllCAFsCC this_mod coreTopBindsToStg :: DynFlags -> Module -> IdEnv HowBound -- environment for the bindings + -> CollectedCCs -> CoreProgram - -> (IdEnv HowBound, FreeVarsInfo, [StgTopBinding]) + -> (IdEnv HowBound, FreeVarsInfo, CollectedCCs, [StgTopBinding]) -coreTopBindsToStg _ _ env [] = (env, emptyFVInfo, []) -coreTopBindsToStg dflags this_mod env (b:bs) - = (env2, fvs2, b':bs') +coreTopBindsToStg _ _ env ccs [] + = (env, emptyFVInfo, ccs, []) +coreTopBindsToStg dflags this_mod env ccs (b:bs) + = (env2, fvs2, ccs2, b':bs') where -- Notice the mutually-recursive "knot" here: -- env accumulates down the list of binds, -- fvs accumulates upwards - (env1, fvs2, b' ) = coreTopBindToStg dflags this_mod env fvs1 b - (env2, fvs1, bs') = coreTopBindsToStg dflags this_mod env1 bs + (env1, fvs2, ccs1, b' ) = + coreTopBindToStg dflags this_mod env fvs1 ccs b + (env2, fvs1, ccs2, bs') = + coreTopBindsToStg dflags this_mod env1 ccs1 bs coreTopBindToStg :: DynFlags -> Module -> IdEnv HowBound -> FreeVarsInfo -- Info about the body + -> CollectedCCs -> CoreBind - -> (IdEnv HowBound, FreeVarsInfo, StgTopBinding) + -> (IdEnv HowBound, FreeVarsInfo, CollectedCCs, StgTopBinding) -coreTopBindToStg _ _ env body_fvs (NonRec id (Lit (MachStr str))) +coreTopBindToStg _ _ env body_fvs ccs (NonRec id e) + | Just str <- exprIsTickedString_maybe e -- top-level string literal + -- See Note [CoreSyn top-level string literals] in CoreSyn = let env' = extendVarEnv env id how_bound how_bound = LetBound TopLet 0 - in (env', body_fvs, StgTopStringLit id str) + in (env', body_fvs, ccs, StgTopStringLit id str) -coreTopBindToStg dflags this_mod env body_fvs (NonRec id rhs) +coreTopBindToStg dflags this_mod env body_fvs ccs (NonRec id rhs) = let env' = extendVarEnv env id how_bound how_bound = LetBound TopLet $! manifestArity rhs - (stg_rhs, fvs') = - initCts env $ do - (stg_rhs, fvs') <- coreToTopStgRhs dflags this_mod body_fvs (id,rhs) - return (stg_rhs, fvs') + (stg_rhs, fvs', ccs') = + initCts env $ + coreToTopStgRhs dflags ccs this_mod body_fvs (id,rhs) bind = StgTopLifted $ StgNonRec id stg_rhs in @@ -257,9 +291,9 @@ coreTopBindToStg dflags this_mod env body_fvs (NonRec id rhs) -- as well as 'id', but that led to a black hole -- where printing the assertion error tripped the -- assertion again! - (env', fvs' `unionFVInfo` body_fvs, bind) + (env', fvs' `unionFVInfo` body_fvs, ccs', bind) -coreTopBindToStg dflags this_mod env body_fvs (Rec pairs) +coreTopBindToStg dflags this_mod env body_fvs ccs (Rec pairs) = ASSERT( not (null pairs) ) let binders = map fst pairs @@ -268,16 +302,21 @@ coreTopBindToStg dflags this_mod env body_fvs (Rec pairs) | (b, rhs) <- pairs ] env' = extendVarEnvList env extra_env' - (stg_rhss, fvs') + -- generate StgTopBindings, accumulate body_fvs and CAF cost centres + -- created for CAFs + ((fvs', ccs'), stg_rhss) = initCts env' $ do - (stg_rhss, fvss') <- mapAndUnzipM (coreToTopStgRhs dflags this_mod body_fvs) pairs - let fvs' = unionFVInfos fvss' - return (stg_rhss, fvs') + mapAccumLM (\(fvs, ccs) rhs -> do + (rhs', fvs', ccs') <- + coreToTopStgRhs dflags ccs this_mod body_fvs rhs + return ((fvs' `unionFVInfo` fvs, ccs'), rhs')) + (body_fvs, ccs) + pairs bind = StgTopLifted $ StgRec (zip binders stg_rhss) in ASSERT2(consistentCafInfo (head binders) bind, ppr binders) - (env', fvs' `unionFVInfo` body_fvs, bind) + (env', fvs' `unionFVInfo` body_fvs, ccs', bind) -- Assertion helper: this checks that the CafInfo on the Id matches @@ -297,18 +336,23 @@ consistentCafInfo id bind coreToTopStgRhs :: DynFlags + -> CollectedCCs -> Module -> FreeVarsInfo -- Free var info for the scope of the binding -> (Id,CoreExpr) - -> CtsM (StgRhs, FreeVarsInfo) + -> CtsM (StgRhs, FreeVarsInfo, CollectedCCs) -coreToTopStgRhs dflags this_mod scope_fv_info (bndr, rhs) +coreToTopStgRhs dflags ccs this_mod scope_fv_info (bndr, rhs) = do { (new_rhs, rhs_fvs) <- coreToStgExpr rhs - ; let stg_rhs = mkTopStgRhs dflags this_mod rhs_fvs bndr bndr_info new_rhs - stg_arity = stgRhsArity stg_rhs + ; let (stg_rhs, ccs') = + mkTopStgRhs dflags this_mod ccs rhs_fvs bndr bndr_info new_rhs + stg_arity = + stgRhsArity stg_rhs + ; return (ASSERT2( arity_ok stg_arity, mk_arity_msg stg_arity) stg_rhs, - rhs_fvs) } + rhs_fvs, + ccs') } where bndr_info = lookupFVInfo scope_fv_info bndr @@ -331,14 +375,6 @@ coreToTopStgRhs dflags this_mod scope_fv_info (bndr, rhs) text "Id arity:" <+> ppr id_arity, text "STG arity:" <+> ppr stg_arity] -mkTopStgRhs :: DynFlags -> Module -> FreeVarsInfo - -> Id -> StgBinderInfo -> StgExpr - -> StgRhs - -mkTopStgRhs dflags this_mod = mkStgRhs' con_updateable - -- Dynamic StgConApps are updatable - where con_updateable con args = isDllConApp dflags this_mod con args - -- --------------------------------------------------------------------------- -- Expressions -- --------------------------------------------------------------------------- @@ -354,9 +390,10 @@ coreToStgExpr -- on these components, but it in turn is not scrutinised as the basis for any -- decisions. Hence no black holes. --- No LitInteger's should be left by the time this is called. CorePrep --- should have converted them all to a real core representation. -coreToStgExpr (Lit (LitInteger {})) = panic "coreToStgExpr: LitInteger" +-- No LitInteger's or LitNatural's should be left by the time this is called. +-- CorePrep should have converted them all to a real core representation. +coreToStgExpr (Lit (LitNumber LitNumInteger _ _)) = panic "coreToStgExpr: LitInteger" +coreToStgExpr (Lit (LitNumber LitNumNatural _ _)) = panic "coreToStgExpr: LitNatural" coreToStgExpr (Lit l) = return (StgLit l, emptyFVInfo) coreToStgExpr (Var v) = coreToStgApp Nothing v [] [] coreToStgExpr (Coercion _) = coreToStgApp Nothing coercionTokenId [] [] @@ -374,9 +411,10 @@ coreToStgExpr expr@(Lam _ _) extendVarEnvCts [ (a, LambdaBound) | a <- args' ] $ do (body, body_fvs) <- coreToStgExpr body let - fvs = args' `minusFVBinders` body_fvs - result_expr | null args' = body - | otherwise = StgLam args' body + fvs = args' `minusFVBinders` body_fvs + result_expr = case nonEmpty args' of + Nothing -> body + Just args'' -> StgLam args'' body return (result_expr, fvs) @@ -718,36 +756,85 @@ coreToStgRhs scope_fv_info (bndr, rhs) = do where bndr_info = lookupFVInfo scope_fv_info bndr -mkStgRhs :: FreeVarsInfo -> Id -> StgBinderInfo -> StgExpr -> StgRhs -mkStgRhs = mkStgRhs' con_updateable - where con_updateable _ _ = False +-- Generate a top-level RHS. Any new cost centres generated for CAFs will be +-- appended to `CollectedCCs` argument. +mkTopStgRhs :: DynFlags -> Module -> CollectedCCs + -> FreeVarsInfo -> Id -> StgBinderInfo -> StgExpr + -> (StgRhs, CollectedCCs) -mkStgRhs' :: (DataCon -> [StgArg] -> Bool) - -> FreeVarsInfo -> Id -> StgBinderInfo -> StgExpr -> StgRhs -mkStgRhs' con_updateable rhs_fvs bndr binder_info rhs +mkTopStgRhs dflags this_mod ccs rhs_fvs bndr binder_info rhs | StgLam bndrs body <- rhs - = StgRhsClosure noCCS binder_info - (getFVs rhs_fvs) - ReEntrant - bndrs body - | isJoinId bndr -- must be nullary join point - = ASSERT(idJoinArity bndr == 0) - StgRhsClosure noCCS binder_info - (getFVs rhs_fvs) - ReEntrant -- ignored for LNE - [] rhs + = -- StgLam can't have empty arguments, so not CAF + ( StgRhsClosure dontCareCCS binder_info + (getFVs rhs_fvs) + ReEntrant + (toList bndrs) body + , ccs ) + | StgConApp con args _ <- unticked_rhs - , not (con_updateable con args) + , -- Dynamic StgConApps are updatable + not (isDllConApp dflags this_mod con args) = -- CorePrep does this right, but just to make sure ASSERT2( not (isUnboxedTupleCon con || isUnboxedSumCon con) , ppr bndr $$ ppr con $$ ppr args) - StgRhsCon noCCS con args + ( StgRhsCon dontCareCCS con args, ccs ) + + -- Otherwise it's a CAF, see Note [Cost-centre initialization plan]. + | gopt Opt_AutoSccsOnIndividualCafs dflags + = ( StgRhsClosure caf_ccs binder_info + (getFVs rhs_fvs) + upd_flag [] rhs + , collectCC caf_cc caf_ccs ccs ) + | otherwise - = StgRhsClosure noCCS binder_info - (getFVs rhs_fvs) - upd_flag [] rhs - where + = ( StgRhsClosure all_cafs_ccs binder_info + (getFVs rhs_fvs) + upd_flag [] rhs + , ccs ) + + where + (_, unticked_rhs) = stripStgTicksTop (not . tickishIsCode) rhs + + upd_flag | isUsedOnce (idDemandInfo bndr) = SingleEntry + | otherwise = Updatable + + -- CAF cost centres generated for -fcaf-all + caf_cc = mkAutoCC bndr modl + caf_ccs = mkSingletonCCS caf_cc + -- careful: the binder might be :Main.main, + -- which doesn't belong to module mod_name. + -- bug #249, tests prof001, prof002 + modl | Just m <- nameModule_maybe (idName bndr) = m + | otherwise = this_mod + + -- default CAF cost centre + (_, all_cafs_ccs) = getAllCAFsCC this_mod + +-- Generate a non-top-level RHS. Cost-centre is always currentCCS, +-- see Note [Cost-centre initialzation plan]. +mkStgRhs :: FreeVarsInfo -> Id -> StgBinderInfo -> StgExpr -> StgRhs +mkStgRhs rhs_fvs bndr binder_info rhs + | StgLam bndrs body <- rhs + = StgRhsClosure currentCCS binder_info + (getFVs rhs_fvs) + ReEntrant + (toList bndrs) body + + | isJoinId bndr -- must be a nullary join point + = ASSERT(idJoinArity bndr == 0) + StgRhsClosure currentCCS binder_info + (getFVs rhs_fvs) + ReEntrant -- ignored for LNE + [] rhs + + | StgConApp con args _ <- unticked_rhs + = StgRhsCon currentCCS con args + | otherwise + = StgRhsClosure currentCCS binder_info + (getFVs rhs_fvs) + upd_flag [] rhs + where (_, unticked_rhs) = stripStgTicksTop (not . tickishIsCode) rhs upd_flag | isUsedOnce (idDemandInfo bndr) = SingleEntry @@ -903,6 +990,14 @@ lookupBinding env v = case lookupVarEnv env v of Just xx -> xx Nothing -> ASSERT2( isGlobalId v, ppr v ) ImportBound +getAllCAFsCC :: Module -> (CostCentre, CostCentreStack) +getAllCAFsCC this_mod = + let + span = mkGeneralSrcSpan (mkFastString "<entire-module>") -- XXX do better + all_cafs_cc = mkAllCafsCC this_mod span + all_cafs_ccs = mkSingletonCCS all_cafs_cc + in + (all_cafs_cc, all_cafs_ccs) -- --------------------------------------------------------------------------- -- Free variable information diff --git a/compiler/stgSyn/StgLint.hs b/compiler/stgSyn/StgLint.hs index cbfd11b8d9..58f14a1b3f 100644 --- a/compiler/stgSyn/StgLint.hs +++ b/compiler/stgSyn/StgLint.hs @@ -1,74 +1,80 @@ -{- +{- | (c) The GRASP/AQUA Project, Glasgow University, 1993-1998 -\section[StgLint]{A ``lint'' pass to check for Stg correctness} --} +A lint pass to check basic STG invariants: + +- Variables should be defined before used. + +- Let bindings should not have unboxed types (unboxed bindings should only + appear in case), except when they're join points (see Note [CoreSyn let/app + invariant] and #14117). + +- If linting after unarisation, invariants listed in Note [Post-unarisation + invariants]. + +Because we don't have types and coercions in STG we can't really check types +here. + +Some history: -{-# LANGUAGE CPP #-} +StgLint used to check types, but it never worked and so it was disabled in 2000 +with this note: + + WARNING: + ~~~~~~~~ + + This module has suffered bit-rot; it is likely to yield lint errors + for Stg code that is currently perfectly acceptable for code + generation. Solution: don't use it! (KSW 2000-05). + +Since then there were some attempts at enabling it again, as summarised in +#14787. It's finally decided that we remove all type checking and only look for +basic properties listed above. +-} module StgLint ( lintStgTopBindings ) where +import GhcPrelude + import StgSyn +import DynFlags import Bag ( Bag, emptyBag, isEmptyBag, snocBag, bagToList ) -import Id ( Id, idType, isLocalId ) +import Id ( Id, idType, isLocalId, isJoinId ) import VarSet import DataCon import CoreSyn ( AltCon(..) ) -import PrimOp ( primOpType ) -import Literal ( literalType ) -import Maybes import Name ( getSrcLoc ) import ErrUtils ( MsgDoc, Severity(..), mkLocMessage ) import Type import RepType -import TyCon -import Util import SrcLoc import Outputable +import qualified ErrUtils as Err +import Control.Applicative ((<|>)) import Control.Monad -#include "HsVersions.h" - -{- -Checks for - (a) *some* type errors - (b) locally-defined variables used but not defined - - -Note: unless -dverbose-stg is on, display of lint errors will result -in "panic: bOGUS_LVs". - -WARNING: -~~~~~~~~ - -This module has suffered bit-rot; it is likely to yield lint errors -for Stg code that is currently perfectly acceptable for code -generation. Solution: don't use it! (KSW 2000-05). - - -************************************************************************ -* * -\subsection{``lint'' for various constructs} -* * -************************************************************************ - -@lintStgTopBindings@ is the top-level interface function. --} +lintStgTopBindings :: DynFlags + -> Bool -- ^ have we run Unarise yet? + -> String -- ^ who produced the STG? + -> [StgTopBinding] + -> IO () -lintStgTopBindings :: String -> [StgTopBinding] -> [StgTopBinding] - -lintStgTopBindings whodunnit binds +lintStgTopBindings dflags unarised whodunnit binds = {-# SCC "StgLint" #-} - case (initL (lint_binds binds)) of - Nothing -> binds - Just msg -> pprPanic "" (vcat [ - text "*** Stg Lint ErrMsgs: in" <+> - text whodunnit <+> text "***", - msg, - text "*** Offending Program ***", - pprStgTopBindings binds, - text "*** End of Offense ***"]) + case initL unarised (lint_binds binds) of + Nothing -> + return () + Just msg -> do + putLogMsg dflags NoReason Err.SevDump noSrcSpan + (defaultDumpStyle dflags) + (vcat [ text "*** Stg Lint ErrMsgs: in" <+> + text whodunnit <+> text "***", + msg, + text "*** Offending Program ***", + pprStgTopBindings binds, + text "*** End of Offense ***"]) + Err.ghcExit dflags 1 where lint_binds :: [StgTopBinding] -> LintM () @@ -81,13 +87,12 @@ lintStgTopBindings whodunnit binds lint_bind (StgTopLifted bind) = lintStgBinds bind lint_bind (StgTopStringLit v _) = return [v] -lintStgArg :: StgArg -> LintM (Maybe Type) -lintStgArg (StgLitArg lit) = return (Just (literalType lit)) -lintStgArg (StgVarArg v) = lintStgVar v +lintStgArg :: StgArg -> LintM () +lintStgArg (StgLitArg _) = return () +lintStgArg (StgVarArg v) = lintStgVar v -lintStgVar :: Id -> LintM (Maybe Kind) -lintStgVar v = do checkInScope v - return (Just (idType v)) +lintStgVar :: Id -> LintM () +lintStgVar id = checkInScope id lintStgBinds :: StgBinding -> LintM [Id] -- Returns the binders lintStgBinds (StgNonRec binder rhs) = do @@ -104,80 +109,50 @@ lintStgBinds (StgRec pairs) lint_binds_help :: (Id, StgRhs) -> LintM () lint_binds_help (binder, rhs) = addLoc (RhsOf binder) $ do - -- Check the rhs - _maybe_rhs_ty <- lintStgRhs rhs - - -- Check binder doesn't have unlifted type - checkL (not (isUnliftedType binder_ty)) + lintStgRhs rhs + -- Check binder doesn't have unlifted type or it's a join point + checkL (isJoinId binder || not (isUnliftedType (idType binder))) (mkUnliftedTyMsg binder rhs) - -- Check match to RHS type - -- Actually we *can't* check the RHS type, because - -- unsafeCoerce means it really might not match at all - -- notably; eg x::Int = (error @Bool "urk") |> unsafeCoerce... - -- case maybe_rhs_ty of - -- Nothing -> return () - -- Just rhs_ty -> checkTys binder_ty - -- rhs_ty - --- (mkRhsMsg binder rhs_ty) - - return () - where - binder_ty = idType binder - -lintStgRhs :: StgRhs -> LintM (Maybe Type) -- Just ty => type is exact +lintStgRhs :: StgRhs -> LintM () lintStgRhs (StgRhsClosure _ _ _ _ [] expr) = lintStgExpr expr lintStgRhs (StgRhsClosure _ _ _ _ binders expr) = addLoc (LambdaBodyOf binders) $ - addInScopeVars binders $ runMaybeT $ do - body_ty <- MaybeT $ lintStgExpr expr - return (mkFunTys (map idType binders) body_ty) + addInScopeVars binders $ + lintStgExpr expr lintStgRhs rhs@(StgRhsCon _ con args) = do - -- TODO: Check arg_tys when (isUnboxedTupleCon con || isUnboxedSumCon con) $ addErrL (text "StgRhsCon is an unboxed tuple or sum application" $$ ppr rhs) - runMaybeT $ do - arg_tys <- mapM (MaybeT . lintStgArg) args - MaybeT $ checkFunApp con_ty arg_tys (mkRhsConMsg con_ty arg_tys) - where - con_ty = dataConRepType con - -lintStgExpr :: StgExpr -> LintM (Maybe Type) -- Just ty => type is exact + mapM_ lintStgArg args + mapM_ checkPostUnariseConArg args -lintStgExpr (StgLit l) = return (Just (literalType l)) +lintStgExpr :: StgExpr -> LintM () -lintStgExpr e@(StgApp fun args) = runMaybeT $ do - fun_ty <- MaybeT $ lintStgVar fun - arg_tys <- mapM (MaybeT . lintStgArg) args - MaybeT $ checkFunApp fun_ty arg_tys (mkFunAppMsg fun_ty arg_tys e) +lintStgExpr (StgLit _) = return () -lintStgExpr e@(StgConApp con args _arg_tys) = runMaybeT $ do - -- TODO: Check arg_tys - arg_tys <- mapM (MaybeT . lintStgArg) args - MaybeT $ checkFunApp con_ty arg_tys (mkFunAppMsg con_ty arg_tys e) - where - con_ty = dataConRepType con +lintStgExpr (StgApp fun args) = do + lintStgVar fun + mapM_ lintStgArg args -lintStgExpr e@(StgOpApp (StgPrimOp op) args _) = runMaybeT $ do - arg_tys <- mapM (MaybeT . lintStgArg) args - MaybeT $ checkFunApp op_ty arg_tys (mkFunAppMsg op_ty arg_tys e) - where - op_ty = primOpType op +lintStgExpr app@(StgConApp con args _arg_tys) = do + -- unboxed sums should vanish during unarise + lf <- getLintFlags + when (lf_unarised lf && isUnboxedSumCon con) $ + addErrL (text "Unboxed sum after unarise:" $$ + ppr app) + mapM_ lintStgArg args + mapM_ checkPostUnariseConArg args -lintStgExpr (StgOpApp _ args res_ty) = runMaybeT $ do - -- We don't have enough type information to check - -- the application for StgFCallOp and StgPrimCallOp; ToDo - _maybe_arg_tys <- mapM (MaybeT . lintStgArg) args - return res_ty +lintStgExpr (StgOpApp _ args _) = + mapM_ lintStgArg args -lintStgExpr (StgLam bndrs _) = do - addErrL (text "Unexpected StgLam" <+> ppr bndrs) - return Nothing +lintStgExpr lam@(StgLam _ _) = + addErrL (text "Unexpected StgLam" <+> ppr lam) lintStgExpr (StgLet binds body) = do binders <- lintStgBinds binds @@ -193,78 +168,25 @@ lintStgExpr (StgLetNoEscape binds body) = do lintStgExpr (StgTick _ expr) = lintStgExpr expr -lintStgExpr (StgCase scrut bndr alts_type alts) = runMaybeT $ do - _ <- MaybeT $ lintStgExpr scrut +lintStgExpr (StgCase scrut bndr alts_type alts) = do + lintStgExpr scrut - in_scope <- MaybeT $ liftM Just $ - case alts_type of - AlgAlt tc -> check_bndr (tyConPrimRep tc) >> return True - PrimAlt rep -> check_bndr [rep] >> return True - MultiValAlt _ -> return False -- Binder is always dead in this case - PolyAlt -> return True + lf <- getLintFlags + let in_scope = stgCaseBndrInScope alts_type (lf_unarised lf) - MaybeT $ addInScopeVars [bndr | in_scope] $ - lintStgAlts alts scrut_ty - where - scrut_ty = idType bndr - scrut_reps = typePrimRep scrut_ty - check_bndr reps = checkL (scrut_reps == reps) bad_bndr - where - bad_bndr = mkDefltMsg bndr reps - -lintStgAlts :: [StgAlt] - -> Type -- Type of scrutinee - -> LintM (Maybe Type) -- Just ty => type is accurage - -lintStgAlts alts scrut_ty = do - maybe_result_tys <- mapM (lintAlt scrut_ty) alts - - -- Check the result types - case catMaybes (maybe_result_tys) of - [] -> return Nothing - - (first_ty:_tys) -> do -- mapM_ check tys - return (Just first_ty) - where - -- check ty = checkTys first_ty ty (mkCaseAltMsg alts) - -- We can't check that the alternatives have the - -- same type, because they don't, with unsafeCoerce# - -lintAlt :: Type -> (AltCon, [Id], StgExpr) -> LintM (Maybe Type) -lintAlt _ (DEFAULT, _, rhs) - = lintStgExpr rhs - -lintAlt scrut_ty (LitAlt lit, _, rhs) = do - checkTys (literalType lit) scrut_ty (mkAltMsg1 scrut_ty) - lintStgExpr rhs - -lintAlt scrut_ty (DataAlt con, args, rhs) = do - case splitTyConApp_maybe scrut_ty of - Just (tycon, tys_applied) | isAlgTyCon tycon && - not (isNewTyCon tycon) -> do - let - cons = tyConDataCons tycon - arg_tys = dataConInstArgTys con tys_applied - -- This does not work for existential constructors - - checkL (con `elem` cons) (mkAlgAltMsg2 scrut_ty con) - checkL (args `lengthIs` dataConRepArity con) (mkAlgAltMsg3 con args) - when (isVanillaDataCon con) $ - mapM_ check (zipEqual "lintAlgAlt:stg" arg_tys args) - return () - _ -> - addErrL (mkAltMsg1 scrut_ty) - - addInScopeVars args $ - lintStgExpr rhs - where - check (ty, arg) = checkTys ty (idType arg) (mkAlgAltMsg4 ty arg) + addInScopeVars [bndr | in_scope] (mapM_ lintAlt alts) + +lintAlt :: (AltCon, [Id], StgExpr) -> LintM () + +lintAlt (DEFAULT, _, rhs) = + lintStgExpr rhs - -- elem: yes, the elem-list here can sometimes be long-ish, - -- but as it's use-once, probably not worth doing anything different - -- We give it its own copy, so it isn't overloaded. - elem _ [] = False - elem x (y:ys) = x==y || elem x ys +lintAlt (LitAlt _, _, rhs) = + lintStgExpr rhs + +lintAlt (DataAlt _, bndrs, rhs) = do + mapM_ checkPostUnariseBndr bndrs + addInScopeVars bndrs (lintStgExpr rhs) {- ************************************************************************ @@ -275,12 +197,17 @@ lintAlt scrut_ty (DataAlt con, args, rhs) = do -} newtype LintM a = LintM - { unLintM :: [LintLocInfo] -- Locations + { unLintM :: LintFlags + -> [LintLocInfo] -- Locations -> IdSet -- Local vars in scope -> Bag MsgDoc -- Error messages so far -> (a, Bag MsgDoc) -- Result and error messages (if any) } +data LintFlags = LintFlags { lf_unarised :: !Bool + -- ^ have we run the unariser yet? + } + data LintLocInfo = RhsOf Id -- The variable bound | LambdaBodyOf [Id] -- The lambda-binder @@ -303,20 +230,22 @@ pp_binders bs pp_binder b = hsep [ppr b, dcolon, ppr (idType b)] -initL :: LintM a -> Maybe MsgDoc -initL (LintM m) - = case (m [] emptyVarSet emptyBag) of { (_, errs) -> +initL :: Bool -> LintM a -> Maybe MsgDoc +initL unarised (LintM m) + = case (m lf [] emptyVarSet emptyBag) of { (_, errs) -> if isEmptyBag errs then Nothing else Just (vcat (punctuate blankLine (bagToList errs))) } + where + lf = LintFlags unarised instance Functor LintM where fmap = liftM instance Applicative LintM where - pure a = LintM $ \_loc _scope errs -> (a, errs) + pure a = LintM $ \_lf _loc _scope errs -> (a, errs) (<*>) = ap (*>) = thenL_ @@ -325,21 +254,59 @@ instance Monad LintM where (>>) = (*>) thenL :: LintM a -> (a -> LintM b) -> LintM b -thenL m k = LintM $ \loc scope errs - -> case unLintM m loc scope errs of - (r, errs') -> unLintM (k r) loc scope errs' +thenL m k = LintM $ \lf loc scope errs + -> case unLintM m lf loc scope errs of + (r, errs') -> unLintM (k r) lf loc scope errs' thenL_ :: LintM a -> LintM b -> LintM b -thenL_ m k = LintM $ \loc scope errs - -> case unLintM m loc scope errs of - (_, errs') -> unLintM k loc scope errs' +thenL_ m k = LintM $ \lf loc scope errs + -> case unLintM m lf loc scope errs of + (_, errs') -> unLintM k lf loc scope errs' checkL :: Bool -> MsgDoc -> LintM () checkL True _ = return () checkL False msg = addErrL msg +-- Case alts shouldn't have unboxed sum, unboxed tuple, or void binders. +checkPostUnariseBndr :: Id -> LintM () +checkPostUnariseBndr bndr = do + lf <- getLintFlags + when (lf_unarised lf) $ + forM_ (checkPostUnariseId bndr) $ \unexpected -> + addErrL $ + text "After unarisation, binder " <> + ppr bndr <> text " has " <> text unexpected <> text " type " <> + ppr (idType bndr) + +-- Arguments shouldn't have sum, tuple, or void types. +checkPostUnariseConArg :: StgArg -> LintM () +checkPostUnariseConArg arg = case arg of + StgLitArg _ -> + return () + StgVarArg id -> do + lf <- getLintFlags + when (lf_unarised lf) $ + forM_ (checkPostUnariseId id) $ \unexpected -> + addErrL $ + text "After unarisation, arg " <> + ppr id <> text " has " <> text unexpected <> text " type " <> + ppr (idType id) + +-- Post-unarisation args and case alt binders should not have unboxed tuple, +-- unboxed sum, or void types. Return what the binder is if it is one of these. +checkPostUnariseId :: Id -> Maybe String +checkPostUnariseId id = + let + id_ty = idType id + is_sum, is_tuple, is_void :: Maybe String + is_sum = guard (isUnboxedSumType id_ty) >> return "unboxed sum" + is_tuple = guard (isUnboxedTupleType id_ty) >> return "unboxed tuple" + is_void = guard (isVoidTy id_ty) >> return "void" + in + is_sum <|> is_tuple <|> is_void + addErrL :: MsgDoc -> LintM () -addErrL msg = LintM $ \loc _scope errs -> ((), addErr errs msg loc) +addErrL msg = LintM $ \_lf loc _scope errs -> ((), addErr errs msg loc) addErr :: Bag MsgDoc -> MsgDoc -> [LintLocInfo] -> Bag MsgDoc addErr errs_so_far msg locs @@ -350,185 +317,26 @@ addErr errs_so_far msg locs mk_msg [] = msg addLoc :: LintLocInfo -> LintM a -> LintM a -addLoc extra_loc m = LintM $ \loc scope errs - -> unLintM m (extra_loc:loc) scope errs +addLoc extra_loc m = LintM $ \lf loc scope errs + -> unLintM m lf (extra_loc:loc) scope errs addInScopeVars :: [Id] -> LintM a -> LintM a -addInScopeVars ids m = LintM $ \loc scope errs +addInScopeVars ids m = LintM $ \lf loc scope errs -> let new_set = mkVarSet ids - in unLintM m loc (scope `unionVarSet` new_set) errs - -{- -Checking function applications: we only check that the type has the -right *number* of arrows, we don't actually compare the types. This -is because we can't expect the types to be equal - the type -applications and type lambdas that we use to calculate accurate types -have long since disappeared. --} + in unLintM m lf loc (scope `unionVarSet` new_set) errs -checkFunApp :: Type -- The function type - -> [Type] -- The arg type(s) - -> MsgDoc -- Error message - -> LintM (Maybe Type) -- Just ty => result type is accurate - -checkFunApp fun_ty arg_tys msg - = do { case mb_msg of - Just msg -> addErrL msg - Nothing -> return () - ; return mb_ty } - where - (mb_ty, mb_msg) = cfa True fun_ty arg_tys - - cfa :: Bool -> Type -> [Type] -> (Maybe Type -- Accurate result? - , Maybe MsgDoc) -- Errors? - - cfa accurate fun_ty [] -- Args have run out; that's fine - = (if accurate then Just fun_ty else Nothing, Nothing) - - cfa accurate fun_ty arg_tys@(arg_ty':arg_tys') - | Just (arg_ty, res_ty) <- splitFunTy_maybe fun_ty - = if accurate && not (arg_ty `stgEqType` arg_ty') - then (Nothing, Just msg) -- Arg type mismatch - else cfa accurate res_ty arg_tys' - - | Just (_, fun_ty') <- splitForAllTy_maybe fun_ty - = cfa False fun_ty' arg_tys - - | Just (tc,tc_args) <- splitTyConApp_maybe fun_ty - , isNewTyCon tc - = if tc_args `lengthLessThan` tyConArity tc - then WARN( True, text "cfa: unsaturated newtype" <+> ppr fun_ty $$ msg ) - (Nothing, Nothing) -- This is odd, but I've seen it - else cfa False (newTyConInstRhs tc tc_args) arg_tys - - | Just tc <- tyConAppTyCon_maybe fun_ty - , not (isTypeFamilyTyCon tc) -- Definite error - = (Nothing, Just msg) -- Too many args - - | otherwise - = (Nothing, Nothing) - -stgEqType :: Type -> Type -> Bool --- Compare types, but crudely because we have discarded --- both casts and type applications, so types might look --- different but be the same. So reply "True" if in doubt. --- "False" means that the types are definitely different. --- --- Fundamentally this is a losing battle because of unsafeCoerce - -stgEqType orig_ty1 orig_ty2 - = gos orig_ty1 orig_ty2 - where - gos :: Type -> Type -> Bool - gos ty1 ty2 - -- These have no prim rep - | isRuntimeRepKindedTy ty1 && isRuntimeRepKindedTy ty2 - = True - - -- We have a unary type - | [_] <- reps1, [_] <- reps2 - = go ty1 ty2 - - -- In the case of a tuple just compare prim reps - | otherwise - = reps1 == reps2 - where - reps1 = typePrimRep ty1 - reps2 = typePrimRep ty2 - - go :: UnaryType -> UnaryType -> Bool - go ty1 ty2 - | Just (tc1, tc_args1) <- splitTyConApp_maybe ty1 - , Just (tc2, tc_args2) <- splitTyConApp_maybe ty2 - , let res = if tc1 == tc2 - then equalLength tc_args1 tc_args2 - && and (zipWith gos tc_args1 tc_args2) - else -- TyCons don't match; but don't bleat if either is a - -- family TyCon because a coercion might have made it - -- equal to something else - (isFamilyTyCon tc1 || isFamilyTyCon tc2) - = if res then True - else - pprTrace "stgEqType: unequal" (vcat [ppr ty1, ppr ty2]) - False - - | otherwise = True -- Conservatively say "fine". - -- Type variables in particular +getLintFlags :: LintM LintFlags +getLintFlags = LintM $ \lf _loc _scope errs -> (lf, errs) checkInScope :: Id -> LintM () -checkInScope id = LintM $ \loc scope errs +checkInScope id = LintM $ \_lf loc scope errs -> if isLocalId id && not (id `elemVarSet` scope) then - ((), addErr errs (hsep [ppr id, text "is out of scope"]) loc) + ((), addErr errs (hsep [ppr id, dcolon, ppr (idType id), + text "is out of scope"]) loc) else ((), errs) -checkTys :: Type -> Type -> MsgDoc -> LintM () -checkTys ty1 ty2 msg = LintM $ \loc _scope errs - -> if (ty1 `stgEqType` ty2) - then ((), errs) - else ((), addErr errs msg loc) - -_mkCaseAltMsg :: [StgAlt] -> MsgDoc -_mkCaseAltMsg _alts - = ($$) (text "In some case alternatives, type of alternatives not all same:") - (Outputable.empty) -- LATER: ppr alts - -mkDefltMsg :: Id -> [PrimRep] -> MsgDoc -mkDefltMsg bndr reps - = ($$) (text "Binder of a case expression doesn't match representation of scrutinee:") - (ppr bndr $$ ppr (idType bndr) $$ ppr reps) - -mkFunAppMsg :: Type -> [Type] -> StgExpr -> MsgDoc -mkFunAppMsg fun_ty arg_tys expr - = vcat [text "In a function application, function type doesn't match arg types:", - hang (text "Function type:") 4 (ppr fun_ty), - hang (text "Arg types:") 4 (vcat (map (ppr) arg_tys)), - hang (text "Expression:") 4 (ppr expr)] - -mkRhsConMsg :: Type -> [Type] -> MsgDoc -mkRhsConMsg fun_ty arg_tys - = vcat [text "In a RHS constructor application, con type doesn't match arg types:", - hang (text "Constructor type:") 4 (ppr fun_ty), - hang (text "Arg types:") 4 (vcat (map (ppr) arg_tys))] - -mkAltMsg1 :: Type -> MsgDoc -mkAltMsg1 ty - = ($$) (text "In a case expression, type of scrutinee does not match patterns") - (ppr ty) - -mkAlgAltMsg2 :: Type -> DataCon -> MsgDoc -mkAlgAltMsg2 ty con - = vcat [ - text "In some algebraic case alternative, constructor is not a constructor of scrutinee type:", - ppr ty, - ppr con - ] - -mkAlgAltMsg3 :: DataCon -> [Id] -> MsgDoc -mkAlgAltMsg3 con alts - = vcat [ - text "In some algebraic case alternative, number of arguments doesn't match constructor:", - ppr con, - ppr alts - ] - -mkAlgAltMsg4 :: Type -> Id -> MsgDoc -mkAlgAltMsg4 ty arg - = vcat [ - text "In some algebraic case alternative, type of argument doesn't match data constructor:", - ppr ty, - ppr arg - ] - -_mkRhsMsg :: Id -> Type -> MsgDoc -_mkRhsMsg binder ty - = vcat [hsep [text "The type of this binder doesn't match the type of its RHS:", - ppr binder], - hsep [text "Binder's type:", ppr (idType binder)], - hsep [text "Rhs type:", ppr ty] - ] - mkUnliftedTyMsg :: Id -> StgRhs -> SDoc mkUnliftedTyMsg binder rhs = (text "Let(rec) binder" <+> quotes (ppr binder) <+> diff --git a/compiler/stgSyn/StgSyn.hs b/compiler/stgSyn/StgSyn.hs index 15181f3e5d..eb905f7456 100644 --- a/compiler/stgSyn/StgSyn.hs +++ b/compiler/stgSyn/StgSyn.hs @@ -39,12 +39,15 @@ module StgSyn ( isDllConApp, stgArgType, stripStgTicksTop, + stgCaseBndrInScope, pprStgBinding, pprStgTopBindings ) where #include "HsVersions.h" +import GhcPrelude + import CoreSyn ( AltCon, Tickish ) import CostCentre ( CostCentreStack ) import Data.ByteString ( ByteString ) @@ -68,6 +71,8 @@ import RepType ( typePrimRep1 ) import Unique ( Unique ) import Util +import Data.List.NonEmpty ( NonEmpty, toList ) + {- ************************************************************************ * * @@ -151,6 +156,18 @@ stripStgTicksTop p = go [] where go ts (StgTick t e) | p t = go (t:ts) e go ts other = (reverse ts, other) +-- | Given an alt type and whether the program is unarised, return whether the +-- case binder is in scope. +-- +-- Case binders of unboxed tuple or unboxed sum type always dead after the +-- unariser has run. See Note [Post-unarisation invariants]. +stgCaseBndrInScope :: AltType -> Bool {- ^ unarised? -} -> Bool +stgCaseBndrInScope alt_ty unarised = + case alt_ty of + AlgAlt _ -> True + PrimAlt _ -> True + MultiValAlt _ -> not unarised + PolyAlt -> True {- ************************************************************************ @@ -219,7 +236,7 @@ finished it encodes (\x -> e) as (let f = \x -> e in f) -} | StgLam - [bndr] + (NonEmpty bndr) StgExpr -- Body of lambda {- @@ -547,6 +564,7 @@ data AltType = PolyAlt -- Polymorphic (a lifted type variable) | MultiValAlt Int -- Multi value of this arity (unboxed tuple or sum) -- the arity could indeed be 1 for unary unboxed tuple + -- or enum-like unboxed sums | AlgAlt TyCon -- Algebraic data type; the AltCons will be DataAlts | PrimAlt PrimRep -- Primitive data type; the AltCons (if any) will be LitAlts @@ -665,8 +683,8 @@ pprGenStgBinding (StgNonRec bndr rhs) 4 (ppr rhs <> semi) pprGenStgBinding (StgRec pairs) - = vcat $ ifPprDebug (text "{- StgRec (begin) -}") : - map (ppr_bind) pairs ++ [ifPprDebug (text "{- StgRec (end) -}")] + = vcat $ whenPprDebug (text "{- StgRec (begin) -}") : + map (ppr_bind) pairs ++ [whenPprDebug (text "{- StgRec (end) -}")] where ppr_bind (bndr, expr) = hang (hsep [pprBndr LetBind bndr, equals]) @@ -718,7 +736,7 @@ pprStgExpr (StgOpApp op args _) = hsep [ pprStgOp op, brackets (interppSP args)] pprStgExpr (StgLam bndrs body) - = sep [ char '\\' <+> ppr_list (map (pprBndr LambdaBind) bndrs) + = sep [ char '\\' <+> ppr_list (map (pprBndr LambdaBind) (toList bndrs)) <+> text "->", pprStgExpr body ] where ppr_list = brackets . fsep . punctuate comma @@ -738,7 +756,7 @@ pprStgExpr (StgLet srt (StgNonRec bndr (StgRhsClosure cc bi free_vars upd_flag a (hang (hcat [text "let { ", ppr bndr, ptext (sLit " = "), ppr cc, pp_binder_info bi, - text " [", ifPprDebug (interppSP free_vars), ptext (sLit "] \\"), + text " [", whenPprDebug (interppSP free_vars), ptext (sLit "] \\"), ppr upd_flag, text " [", interppSP args, char ']']) 8 (sep [hsep [ppr rhs, text "} in"]])) @@ -774,7 +792,7 @@ pprStgExpr (StgTick tickish expr) pprStgExpr (StgCase expr bndr alt_type alts) = sep [sep [text "case", nest 4 (hsep [pprStgExpr expr, - ifPprDebug (dcolon <+> ppr alt_type)]), + whenPprDebug (dcolon <+> ppr alt_type)]), text "of", pprBndr CaseBind bndr, char '{'], nest 2 (vcat (map pprStgAlt alts)), char '}'] @@ -801,9 +819,11 @@ pprStgRhs :: (OutputableBndr bndr, Outputable bdee, Ord bdee) -- special case pprStgRhs (StgRhsClosure cc bi [free_var] upd_flag [{-no args-}] (StgApp func [])) - = hsep [ ppr cc, + = sdocWithDynFlags $ \dflags -> + hsep [ ppr cc, pp_binder_info bi, - brackets (ifPprDebug (ppr free_var)), + if not $ gopt Opt_SuppressStgFreeVars dflags + then brackets (ppr free_var) else empty, text " \\", ppr upd_flag, ptext (sLit " [] "), ppr func ] -- general case @@ -811,7 +831,8 @@ pprStgRhs (StgRhsClosure cc bi free_vars upd_flag args body) = sdocWithDynFlags $ \dflags -> hang (hsep [if gopt Opt_SccProfilingOn dflags then ppr cc else empty, pp_binder_info bi, - ifPprDebug (brackets (interppSP free_vars)), + if not $ gopt Opt_SuppressStgFreeVars dflags + then brackets (interppSP free_vars) else empty, char '\\' <> ppr upd_flag, brackets (interppSP args)]) 4 (ppr body) |