diff --git a/compiler/coreSyn/CoreUnfold.lhs b/compiler/coreSyn/CoreUnfold.lhs index 0510e90d6db6176183f9a6784761eefd3ab9aa98..f374c005fcfe591db583b59f79e043924f1f02b8 100644 --- a/compiler/coreSyn/CoreUnfold.lhs +++ b/compiler/coreSyn/CoreUnfold.lhs @@ -1,4 +1,4 @@ -calcU% +% % (c) The University of Glasgow 2006 % (c) The AQUA Project, Glasgow University, 1994-1998 % @@ -72,12 +72,13 @@ import Outputable %************************************************************************ \begin{code} -mkTopUnfolding :: CoreExpr -> Unfolding -mkTopUnfolding expr = mkUnfolding True {- Top level -} expr +mkTopUnfolding :: Bool -> CoreExpr -> Unfolding +mkTopUnfolding is_bottoming expr + = mkUnfolding True {- Top level -} is_bottoming expr mkImplicitUnfolding :: CoreExpr -> Unfolding -- For implicit Ids, do a tiny bit of optimising first -mkImplicitUnfolding expr = mkTopUnfolding (simpleOptExpr expr) +mkImplicitUnfolding expr = mkTopUnfolding False (simpleOptExpr expr) -- Note [Top-level flag on inline rules] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -85,8 +86,8 @@ mkImplicitUnfolding expr = mkTopUnfolding (simpleOptExpr expr) -- top-level flag to True. It gets set more accurately by the simplifier -- Simplify.simplUnfolding. -mkUnfolding :: Bool -> CoreExpr -> Unfolding -mkUnfolding top_lvl expr +mkUnfolding :: Bool -> Bool -> CoreExpr -> Unfolding +mkUnfolding top_lvl is_bottoming expr = CoreUnfolding { uf_tmpl = occurAnalyseExpr expr, uf_src = InlineRhs, uf_arity = arity, @@ -98,7 +99,8 @@ mkUnfolding top_lvl expr uf_guidance = guidance } where is_cheap = exprIsCheap expr - (arity, guidance) = calcUnfoldingGuidance is_cheap opt_UF_CreationThreshold expr + (arity, guidance) = calcUnfoldingGuidance is_cheap (top_lvl && is_bottoming) + opt_UF_CreationThreshold expr -- Sometimes during simplification, there's a large let-bound thing -- which has been substituted, and so is now dead; so 'expr' contains -- two copies of the thing while the occurrence-analysed expression doesn't @@ -146,6 +148,7 @@ mkInlineRule unsat_ok expr arity where expr' = simpleOptExpr expr boring_ok = case calcUnfoldingGuidance True -- Treat as cheap + False -- But not bottoming (arity+1) expr' of (_, UnfWhen _ boring_ok) -> boring_ok _other -> boringCxtNotOk @@ -163,10 +166,12 @@ mkInlineRule unsat_ok expr arity calcUnfoldingGuidance :: Bool -- True <=> the rhs is cheap, or we want to treat it -- as cheap (INLINE things) + -> Bool -- True <=> this is a top-level unfolding for a + -- diverging function; don't inline this -> Int -- Bomb out if size gets bigger than this -> CoreExpr -- Expression to look at -> (Arity, UnfoldingGuidance) -calcUnfoldingGuidance expr_is_cheap bOMB_OUT_SIZE expr +calcUnfoldingGuidance expr_is_cheap top_bot bOMB_OUT_SIZE expr = case collectBinders expr of { (bndrs, body) -> let val_bndrs = filter isId bndrs @@ -179,6 +184,9 @@ calcUnfoldingGuidance expr_is_cheap bOMB_OUT_SIZE expr | uncondInline n_val_bndrs (iBox size) && expr_is_cheap -> UnfWhen needSaturated boringCxtOk + | top_bot -- See Note [Do not inline top-level bottoming functions] + -> UnfNever + | otherwise -> UnfIfGoodArgs { ug_args = map (discount cased_bndrs) val_bndrs , ug_size = iBox size @@ -222,6 +230,15 @@ Notice that 'x' counts 0, while (f x) counts 2. That's deliberate: there's a function call to account for. Notice also that constructor applications are very cheap, because exposing them to a caller is so valuable. + +Note [Do not inline top-level bottoming functions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The FloatOut pass has gone to some trouble to float out calls to 'error' +and similar friends. See Note [Bottoming floats] in SetLevels. +Do not re-inline them! But we *do* still inline if they are very small +(the uncondInline stuff). + + Note [Unconditional inlining] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We inline *unconditionally* if inlined thing is smaller (using sizeExpr) @@ -566,7 +583,7 @@ actual arguments. \begin{code} couldBeSmallEnoughToInline :: Int -> CoreExpr -> Bool couldBeSmallEnoughToInline threshold rhs - = case calcUnfoldingGuidance False threshold rhs of + = case calcUnfoldingGuidance False False threshold rhs of (_, UnfNever) -> False _ -> True diff --git a/compiler/iface/MkIface.lhs b/compiler/iface/MkIface.lhs index 897c138406034ce40479b74983875fea6b745e63..1c34edca3ce19ab44b751f025c77215a430144f7 100644 --- a/compiler/iface/MkIface.lhs +++ b/compiler/iface/MkIface.lhs @@ -1472,6 +1472,8 @@ toIfaceIdInfo :: IdInfo -> [IfaceInfoItem] toIfaceIdInfo id_info = catMaybes [arity_hsinfo, caf_hsinfo, strict_hsinfo, inline_hsinfo, unfold_hsinfo] + -- NB: strictness must be before unfolding + -- See TcIface.tcUnfolding where ------------ Arity -------------- arity_info = arityInfo id_info diff --git a/compiler/iface/TcIface.lhs b/compiler/iface/TcIface.lhs index 2ec9de97a0068681314ab900c7ddfe297787e82b..c9c33dbde663e15492758ec1ec8823f7413de621 100644 --- a/compiler/iface/TcIface.lhs +++ b/compiler/iface/TcIface.lhs @@ -46,6 +46,7 @@ import VarEnv import Name import NameEnv import OccurAnal ( occurAnalyseExpr ) +import Demand ( isBottomingSig ) import Module import LazyUniqFM import UniqSupply @@ -1003,11 +1004,16 @@ tcIdInfo ignore_prags name ty info \begin{code} tcUnfolding :: Name -> Type -> IdInfo -> IfaceUnfolding -> IfL Unfolding -tcUnfolding name _ _ (IfCoreUnfold if_expr) +tcUnfolding name _ info (IfCoreUnfold if_expr) = do { mb_expr <- tcPragExpr name if_expr ; return (case mb_expr of Nothing -> NoUnfolding - Just expr -> mkTopUnfolding expr) } + Just expr -> mkTopUnfolding is_bottoming expr) } + where + -- Strictness should occur before unfolding! + is_bottoming = case strictnessInfo info of + Just sig -> isBottomingSig sig + Nothing -> False tcUnfolding name _ _ (IfInlineRule arity unsat_ok if_expr) = do { mb_expr <- tcPragExpr name if_expr @@ -1029,8 +1035,8 @@ tcUnfolding name ty info (IfWrapper arity wkr) (initUs_ us (mkWrapper ty strict_sig) wkr_id) arity - -- We are relying here on strictness info always appearing - -- before worker info, fingers crossed .... + -- Again we rely here on strictness info always appearing + -- before unfolding strict_sig = case strictnessInfo info of Just sig -> sig Nothing -> pprPanic "Worker info but no strictness for" (ppr wkr) diff --git a/compiler/main/TidyPgm.lhs b/compiler/main/TidyPgm.lhs index eefdd2d9aa646a048ad1c3158724c1204a500377..41d9234137ca01c821f5a3d29132db52106798a6 100644 --- a/compiler/main/TidyPgm.lhs +++ b/compiler/main/TidyPgm.lhs @@ -983,21 +983,24 @@ tidyTopPair show_unfold rhs_tidy_env caf_info name' (bndr, rhs) -- the RHS is bottom, it should jolly well be exposed _bottom_exposed = case exprBotStrictness_maybe rhs of Nothing -> True - Just (arity, _) -> appIsBottom str arity + Just (arity, _) -> appIsBottom str_sig arity where - str = strictnessInfo idinfo `orElse` topSig - - bndr1 = mkGlobalId details name' ty' idinfo' - details = idDetails bndr -- Preserve the IdDetails - ty' = tidyTopType (idType bndr) - rhs1 = tidyExpr rhs_tidy_env rhs - idinfo = idInfo bndr - idinfo' = tidyTopIdInfo (isExternalName name') + + + bndr1 = mkGlobalId details name' ty' idinfo' + details = idDetails bndr -- Preserve the IdDetails + ty' = tidyTopType (idType bndr) + rhs1 = tidyExpr rhs_tidy_env rhs + idinfo = idInfo bndr + unf_info = unfoldingInfo idinfo + str_sig = strictnessInfo idinfo `orElse` topSig + is_bot = isBottomingSig str_sig + idinfo' = tidyTopIdInfo (isExternalName name') idinfo unfold_info arity caf_info (occInfo idinfo) - unfold_info | show_unfold = tidyUnfolding rhs_tidy_env rhs1 (unfoldingInfo idinfo) + unfold_info | show_unfold = tidyUnfolding rhs_tidy_env rhs1 is_bot unf_info | otherwise = noUnfolding -- NB: do *not* expose the worker if show_unfold is off, -- because that means this thing is a loop breaker or @@ -1065,16 +1068,17 @@ tidyTopIdInfo is_external idinfo unfold_info arity caf_info occ_info ------------ Unfolding -------------- -tidyUnfolding :: TidyEnv -> CoreExpr -> Unfolding -> Unfolding -tidyUnfolding tidy_env _ (DFunUnfolding con ids) +tidyUnfolding :: TidyEnv -> CoreExpr -> Bool -> Unfolding -> Unfolding +tidyUnfolding tidy_env _ _ (DFunUnfolding con ids) = DFunUnfolding con (map (tidyExpr tidy_env) ids) -tidyUnfolding tidy_env tidy_rhs unf@(CoreUnfolding { uf_tmpl = unf_rhs, uf_src = src }) +tidyUnfolding tidy_env tidy_rhs is_bottoming + unf@(CoreUnfolding { uf_tmpl = unf_rhs, uf_src = src }) | isInlineRuleSource src = unf { uf_tmpl = tidyExpr tidy_env unf_rhs, -- Preserves OccInfo uf_src = tidyInl tidy_env src } | otherwise - = mkTopUnfolding tidy_rhs -tidyUnfolding _ _ unf = unf + = mkTopUnfolding is_bottoming tidy_rhs +tidyUnfolding _ _ _ unf = unf tidyInl :: TidyEnv -> UnfoldingSource -> UnfoldingSource tidyInl tidy_env (InlineWrapper w) = InlineWrapper (tidyVarOcc tidy_env w) diff --git a/compiler/simplCore/FloatOut.lhs b/compiler/simplCore/FloatOut.lhs index d65f7bd17e646b668ebce87eb3dfde880bdccb4c..f5f894648af86776ff78e88326020b7df53ddf47 100644 --- a/compiler/simplCore/FloatOut.lhs +++ b/compiler/simplCore/FloatOut.lhs @@ -10,11 +10,12 @@ module FloatOut ( floatOutwards ) where import CoreSyn import CoreUtils +import CoreArity ( etaExpand ) import DynFlags ( DynFlags, DynFlag(..), FloatOutSwitches(..) ) import ErrUtils ( dumpIfSet_dyn ) import CostCentre ( dupifyCC, CostCentre ) -import Id ( Id, idType ) +import Id ( Id, idType, idArity, isBottomingId ) import Type ( isUnLiftedType ) import SetLevels ( Level(..), LevelledExpr, LevelledBind, setLevels, isTopLvl, tOP_LEVEL ) @@ -144,13 +145,18 @@ floatTopBind bind %* * %************************************************************************ - \begin{code} floatBind :: LevelledBind -> (FloatStats, FloatBinds) -floatBind (NonRec (TB name level) rhs) +floatBind (NonRec (TB var level) rhs) = case (floatRhs level rhs) of { (fs, rhs_floats, rhs') -> - (fs, rhs_floats `plusFloats` unitFloat level (NonRec name rhs')) } + + -- A tiresome hack: + -- see Note [Bottoming floats: eta expansion] in SetLevels + let rhs'' | isBottomingId var = etaExpand (idArity var) rhs' + | otherwise = rhs' + + in (fs, rhs_floats `plusFloats` unitFloat level (NonRec var rhs'')) } floatBind bind@(Rec pairs) = case (unzip3 (map do_pair pairs)) of { (fss, rhss_floats, new_pairs) -> @@ -297,8 +303,8 @@ floatExpr lvl (Cast expr co) (fs, floating_defns, Cast expr' co) } floatExpr lvl (Let (NonRec (TB bndr bndr_lvl) rhs) body) - | isUnLiftedType (idType bndr) -- Treat unlifted lets just like a case - -- I.e. floatExpr for rhs, floatCaseAlt for body + | isUnLiftedType (idType bndr) -- Treat unlifted lets just like a case + -- I.e. floatExpr for rhs, floatCaseAlt for body = case floatExpr lvl rhs of { (_, rhs_floats, rhs') -> case floatCaseAlt bndr_lvl body of { (fs, body_floats, body') -> (fs, rhs_floats `plusFloats` body_floats, Let (NonRec bndr rhs') body') }} diff --git a/compiler/simplCore/SetLevels.lhs b/compiler/simplCore/SetLevels.lhs index ed420899e8102b6aa6f83daa7419899f895ed3da..d0914c948bfd68c97e57096f04e43729dd5e793c 100644 --- a/compiler/simplCore/SetLevels.lhs +++ b/compiler/simplCore/SetLevels.lhs @@ -56,12 +56,12 @@ module SetLevels ( import CoreSyn import DynFlags ( FloatOutSwitches(..) ) -import CoreUtils ( exprType, exprIsTrivial, mkPiTypes ) +import CoreUtils ( exprType, mkPiTypes ) import CoreArity ( exprBotStrictness_maybe ) import CoreFVs -- all of it import CoreSubst ( Subst, emptySubst, extendInScope, extendInScopeList, extendIdSubst, cloneIdBndr, cloneRecIdBndrs ) -import Id ( idType, mkSysLocal, isOneShotLambda, +import Id ( idType, mkLocalIdWithInfo, mkSysLocal, isOneShotLambda, zapDemandIdInfo, transferPolyIdInfo, idSpecialisation, idUnfolding, setIdInfo, setIdStrictness, setIdArity @@ -70,10 +70,11 @@ import IdInfo import Var import VarSet import VarEnv -import Name ( getOccName ) +import Demand ( StrictSig, increaseStrictSigArity ) +import Name ( getOccName, mkSystemVarName ) import OccName ( occNameString ) import Type ( isUnLiftedType, Type ) -import BasicTypes ( TopLevelFlag(..) ) +import BasicTypes ( TopLevelFlag(..), Arity ) import UniqSupply import Util ( sortLe, isSingleton, count ) import Outputable @@ -340,10 +341,25 @@ If we see we'd like to float the call to error, to get lvl = error "urk" f = \x. g lvl -But, it's very helpful for lvl to get a strictness signature, so that, -for example, its unfolding is not exposed in interface files (unnecessary). -But this float-out might occur after strictness analysis. So we use the -cheap-and-cheerful exprBotStrictness_maybe function. +Furthermore, we want to float a bottoming expression even if it has free +variables: + f = \x. g (let v = h x in error ("urk" ++ v)) +Then we'd like to abstact over 'x' can float the whole arg of g: + lvl = \x. let v = h x in error ("urk" ++ v) + f = \x. g (lvl x) +See Maessen's paper 1999 "Bottom extraction: factoring error handling out +of functional programs" (unpublished I think). + +When we do this, we set the strictness and arity of the new bottoming +Id, so that it's properly exposed as such in the interface file, even if +this is all happening after strictness analysis. + +Note [Bottoming floats: eta expansion] c.f Note [Bottoming floats] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Tiresomely, though, the simplifier has an invariant that the manifest +arity of the RHS should be the same as the arity; but we can't call +etaExpand during SetLevels because it works over a decorated form of +CoreExpr. So we do the eta expansion later, in FloatOut. Note [Case MFEs] ~~~~~~~~~~~~~~~~ @@ -381,25 +397,21 @@ lvlMFE True ctxt_lvl env e@(_, AnnCase {}) lvlMFE strict_ctxt ctxt_lvl env ann_expr@(fvs, _) | isUnLiftedType ty -- Can't let-bind it; see Note [Unlifted MFEs] - || exprIsTrivial expr -- Never float if it's trivial + || notWorthFloating ann_expr abs_vars || not good_destination = -- Don't float it out lvlExpr ctxt_lvl env ann_expr | otherwise -- Float it out! = do expr' <- lvlFloatRhs abs_vars dest_lvl env ann_expr - var <- newLvlVar "lvl" abs_vars ty - -- Note [Bottoming floats] - let var_w_str = case exprBotStrictness_maybe expr of - Just (arity,str) -> var `setIdArity` arity - `setIdStrictness` str - Nothing -> var - return (Let (NonRec (TB var_w_str dest_lvl) expr') - (mkVarApps (Var var_w_str) abs_vars)) + var <- newLvlVar abs_vars ty mb_bot + return (Let (NonRec (TB var dest_lvl) expr') + (mkVarApps (Var var) abs_vars)) where expr = deAnnotate ann_expr ty = exprType expr - dest_lvl = destLevel env fvs (isFunction ann_expr) + mb_bot = exprBotStrictness_maybe expr + dest_lvl = destLevel env fvs (isFunction ann_expr) mb_bot abs_vars = abstractVars dest_lvl env fvs -- A decision to float entails let-binding this thing, and we only do @@ -426,6 +438,42 @@ lvlMFE strict_ctxt ctxt_lvl env ann_expr@(fvs, _) -- concat = /\ a -> lvl a -- lvl = /\ a -> foldr ..a.. (++) [] -- which is pretty stupid. Hence the strict_ctxt test + +annotateBotStr :: Id -> Maybe (Arity, StrictSig) -> Id +annotateBotStr id Nothing = id +annotateBotStr id (Just (arity,sig)) = id `setIdArity` arity + `setIdStrictness` sig + +notWorthFloating :: CoreExprWithFVs -> [Var] -> Bool +-- Returns True if the expression would be replaced by +-- something bigger than it is now. For example: +-- abs_vars = tvars only: return True if e is trivial, +-- but False for anything bigger +-- abs_vars = [x] (an Id): return True for trivial, or an application (f x) +-- but False for (f x x) +-- +-- One big goal is that floating should be idempotent. Eg if +-- we replace e with (lvl79 x y) and then run FloatOut again, don't want +-- to replace (lvl79 x y) with (lvl83 x y)! + +notWorthFloating e abs_vars + = go e (count isId abs_vars) + where + go (_, AnnVar {}) n = n == 0 + go (_, AnnLit {}) n = n == 0 + go (_, AnnCast e _) n = go e n + go (_, AnnApp e arg) n + | (_, AnnType {}) <- arg = go e n + | n==0 = False + | is_triv arg = go e (n-1) + | otherwise = False + go _ _ = False + + is_triv (_, AnnLit {}) = True -- Treat all literals as trivial + is_triv (_, AnnVar {}) = True -- (ie not worth floating) + is_triv (_, AnnCast e _) = is_triv e + is_triv (_, AnnApp e (_, AnnType {})) = is_triv e + is_triv _ = False \end{code} Note [Escaping a value lambda] @@ -502,13 +550,15 @@ lvlBind top_lvl ctxt_lvl env (AnnNonRec bndr rhs@(rhs_fvs,_)) | otherwise = do -- Yes, type abstraction; create a new binder, extend substitution, etc rhs' <- lvlFloatRhs abs_vars dest_lvl env rhs - (env', [bndr']) <- newPolyBndrs dest_lvl env abs_vars [bndr] + (env', [bndr']) <- newPolyBndrs dest_lvl env abs_vars [bndr_w_str] return (NonRec (TB bndr' dest_lvl) rhs', env') where - bind_fvs = rhs_fvs `unionVarSet` idFreeVars bndr - abs_vars = abstractVars dest_lvl env bind_fvs - dest_lvl = destLevel env bind_fvs (isFunction rhs) + bind_fvs = rhs_fvs `unionVarSet` idFreeVars bndr + abs_vars = abstractVars dest_lvl env bind_fvs + dest_lvl = destLevel env bind_fvs (isFunction rhs) mb_bot + mb_bot = exprBotStrictness_maybe (deAnnotate rhs) + bndr_w_str = annotateBotStr bndr mb_bot \end{code} @@ -562,7 +612,7 @@ lvlBind top_lvl ctxt_lvl env (AnnRec pairs) `minusVarSet` mkVarSet bndrs - dest_lvl = destLevel env bind_fvs (all isFunction rhss) + dest_lvl = destLevel env bind_fvs (all isFunction rhss) Nothing abs_vars = abstractVars dest_lvl env bind_fvs ---------------------------------------------------- @@ -619,12 +669,14 @@ lvlLamBndrs lvl bndrs \begin{code} -- Destintion level is the max Id level of the expression -- (We'll abstract the type variables, if any.) -destLevel :: LevelEnv -> VarSet -> Bool -> Level -destLevel env fvs is_function +destLevel :: LevelEnv -> VarSet -> Bool -> Maybe (Arity, StrictSig) -> Level +destLevel env fvs is_function mb_bot + | Just {} <- mb_bot = tOP_LEVEL -- Send bottoming bindings to the top + -- regardless; see Note [Bottoming floats] | floatLams env - && is_function = tOP_LEVEL -- Send functions to top level; see + && is_function = tOP_LEVEL -- Send functions to top level; see -- the comments with isFunction - | otherwise = maxIdLevel env fvs + | otherwise = maxIdLevel env fvs isFunction :: CoreExprWithFVs -> Bool -- The idea here is that we want to float *functions* to @@ -857,12 +909,20 @@ newPolyBndrs dest_lvl env abs_vars bndrs = do str = "poly_" ++ occNameString (getOccName bndr) poly_ty = mkPiTypes abs_vars (idType bndr) -newLvlVar :: String - -> [CoreBndr] -> Type -- Abstract wrt these bndrs +newLvlVar :: [CoreBndr] -> Type -- Abstract wrt these bndrs + -> Maybe (Arity, StrictSig) -- Note [Bottoming floats] -> LvlM Id -newLvlVar str vars body_ty = do - uniq <- getUniqueM - return (mkSysLocal (mkFastString str) uniq (mkPiTypes vars body_ty)) +newLvlVar vars body_ty mb_bot + = do { uniq <- getUniqueM + ; return (mkLocalIdWithInfo (mk_name uniq) (mkPiTypes vars body_ty) info) } + where + mk_name uniq = mkSystemVarName uniq (mkFastString "lvl") + arity = count isId vars + info = case mb_bot of + Nothing -> vanillaIdInfo + Just (bot_arity, sig) -> vanillaIdInfo + `setArityInfo` (arity + bot_arity) + `setStrictnessInfo` Just (increaseStrictSigArity arity sig) -- The deeply tiresome thing is that we have to apply the substitution -- to the rules inside each Id. Grr. But it matters. diff --git a/compiler/simplCore/SimplUtils.lhs b/compiler/simplCore/SimplUtils.lhs index 7a5b96b3524aa2033157281ec7a8cc7aeb79da59..56d2795e310d9db0f200e5e3432ee0d2abde31bc 100644 --- a/compiler/simplCore/SimplUtils.lhs +++ b/compiler/simplCore/SimplUtils.lhs @@ -635,11 +635,18 @@ let-float if you inline windowToViewport However, as usual for Gentle mode, do not inline things that are inactive in the intial stages. See Note [Gentle mode]. +Note [Top-level botomming Ids] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Don't inline top-level Ids that are bottoming, even if they are used just +once, because FloatOut has gone to some trouble to extract them out. +Inlining them won't make the program run faster! + \begin{code} preInlineUnconditionally :: SimplEnv -> TopLevelFlag -> InId -> InExpr -> Bool preInlineUnconditionally env top_lvl bndr rhs - | not active = False - | opt_SimplNoPreInlining = False + | not active = False + | isTopLevel top_lvl && isBottomingId bndr = False -- Note [Top-level bottoming Ids] + | opt_SimplNoPreInlining = False | otherwise = case idOccInfo bndr of IAmDead -> True -- Happens in ((\x.1) v) OneOcc in_lam True int_cxt -> try_once in_lam int_cxt @@ -651,12 +658,11 @@ preInlineUnconditionally env top_lvl bndr rhs -- See Note [pre/postInlineUnconditionally in gentle mode] SimplPhase n _ -> isActive n act act = idInlineActivation bndr - try_once in_lam int_cxt -- There's one textual occurrence | not in_lam = isNotTopLevel top_lvl || early_phase | otherwise = int_cxt && canInlineInLam rhs --- Be very careful before inlining inside a lambda, becuase (a) we must not +-- Be very careful before inlining inside a lambda, because (a) we must not -- invalidate occurrence information, and (b) we want to avoid pushing a -- single allocation (here) into multiple allocations (inside lambda). -- Inlining a *function* with a single *saturated* call would be ok, mind you. @@ -745,6 +751,7 @@ postInlineUnconditionally env top_lvl bndr occ_info rhs unfolding | isExportedId bndr = False | isStableUnfolding unfolding = False -- Note [InlineRule and postInlineUnconditionally] | exprIsTrivial rhs = True + | isTopLevel top_lvl = False -- Note [Top level and postInlineUnconditionally] | otherwise = case occ_info of -- The point of examining occ_info here is that for *non-values* @@ -771,8 +778,8 @@ postInlineUnconditionally env top_lvl bndr occ_info rhs unfolding -- PRINCIPLE: when we've already simplified an expression once, -- make sure that we only inline it if it's reasonably small. - && ((isNotTopLevel top_lvl && not in_lam) || - -- But outside a lambda, we want to be reasonably aggressive + && (not in_lam || + -- Outside a lambda, we want to be reasonably aggressive -- about inlining into multiple branches of case -- e.g. let x = <non-value> -- in case y of { C1 -> ..x..; C2 -> ..x..; C3 -> ... } @@ -875,6 +882,14 @@ activeRule dflags env SimplPhase n _ -> Just (isActive n) \end{code} +Note [Top level and postInlineUnconditionally] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We don't do postInlineUnconditionally for top-level things (except +ones that are trivial). There is no point, because the main goal is +to get rid of local bindings used in multiple case branches. And +doing so risks replacing a single global allocation with local allocations. + + Note [InlineRule and postInlineUnconditionally] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Do not do postInlineUnconditionally if the Id has an InlineRule, otherwise diff --git a/compiler/simplCore/Simplify.lhs b/compiler/simplCore/Simplify.lhs index 37fa798965bd6bea2dc323313f0570aabcbfbc63..f6e8569936c67c78da9938fbf739f13031897617 100644 --- a/compiler/simplCore/Simplify.lhs +++ b/compiler/simplCore/Simplify.lhs @@ -662,7 +662,7 @@ addNonRecWithUnf env new_bndr new_rhs new_unfolding ------------------------------ simplUnfolding :: SimplEnv-> TopLevelFlag - -> Id -- Debug output only + -> Id -> OccInfo -> OutExpr -> Unfolding -> SimplM Unfolding -- Note [Setting the new unfolding] @@ -681,8 +681,8 @@ simplUnfolding env top_lvl _ _ _ ; return (mkCoreUnfolding (isTopLevel top_lvl) src' expr' arity guide) } -- See Note [Top-level flag on inline rules] in CoreUnfold -simplUnfolding _ top_lvl _ _occ_info new_rhs _ - = return (mkUnfolding (isTopLevel top_lvl) new_rhs) +simplUnfolding _ top_lvl id _occ_info new_rhs _ + = return (mkUnfolding (isTopLevel top_lvl) (isBottomingId id) new_rhs) -- We make an unfolding *even for loop-breakers*. -- Reason: (a) It might be useful to know that they are WHNF -- (b) In TidyPgm we currently assume that, if we want to @@ -1724,7 +1724,7 @@ simplAlt env _ case_bndr' cont' (DataAlt con, vs, rhs) addBinderUnfolding :: SimplEnv -> Id -> CoreExpr -> SimplEnv addBinderUnfolding env bndr rhs - = modifyInScope env (bndr `setIdUnfolding` mkUnfolding False rhs) + = modifyInScope env (bndr `setIdUnfolding` mkUnfolding False False rhs) addBinderOtherCon :: SimplEnv -> Id -> [AltCon] -> SimplEnv addBinderOtherCon env bndr cons diff --git a/compiler/specialise/Specialise.lhs b/compiler/specialise/Specialise.lhs index d73856585674f815de8cfa3bb0f58558043cf187..ad641d4c93e4419f5cc11f6b7272ea146952a030 100644 --- a/compiler/specialise/Specialise.lhs +++ b/compiler/specialise/Specialise.lhs @@ -939,7 +939,7 @@ bindAuxiliaryDicts subst triples = go subst [] triples -- No auxiliary binding necessary | otherwise = go subst_w_unf (NonRec dx_id dx : binds) pairs where - dx_id1 = dx_id `setIdUnfolding` mkUnfolding False dx + dx_id1 = dx_id `setIdUnfolding` mkUnfolding False False dx subst_w_unf = extendIdSubst subst d (Var dx_id1) -- Important! We're going to substitute dx_id1 for d -- and we want it to look "interesting", else we won't gather *any*