%
-% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
\section[FloatOut]{Float bindings outwards (towards the top level)}
``Long-distance'' floating of bindings towards the top level.
\begin{code}
-#include "HsVersions.h"
-
module FloatOut ( floatOutwards ) where
-IMP_Ubiq(){-uitous-}
-IMPORT_1_3(List(partition))
+#include "HsVersions.h"
import CoreSyn
-
-import CmdLineOpts ( opt_D_verbose_core2core, opt_D_simplifier_stats )
-import CostCentre ( dupifyCC )
-import Id ( nullIdEnv, addOneToIdEnv, growIdEnvList, SYN_IE(IdEnv),
- GenId{-instance Outputable-}
+import CoreUtils ( mkSCC )
+
+import CmdLineOpts ( DynFlags, DynFlag(..) )
+import ErrUtils ( dumpIfSet_dyn )
+import CostCentre ( dupifyCC, CostCentre )
+import Id ( Id )
+import VarEnv
+import CoreLint ( showPass, endPass )
+import SetLevels ( setLevels,
+ Level(..), tOP_LEVEL, ltMajLvl, ltLvl, isTopLvl
)
-import Outputable ( Outputable(..){-instance (,)-} )
-import PprCore
-import PprStyle ( PprStyle(..) )
-import PprType ( GenTyVar )
-import Pretty ( ppInt, ppStr, ppBesides, ppAboves )
-import SetLevels -- all of it
-import TyVar ( GenTyVar{-instance Eq-} )
-import Unique ( Unique{-instance Eq-} )
-import Usage ( SYN_IE(UVar) )
-import Util ( pprTrace, panic )
+import UniqSupply ( UniqSupply )
+import List ( partition )
+import Outputable
\end{code}
Random comments
@
\deq x ys -> let eq = eqFromEqDict deq in ...
@
-which might usefully be separated to
+vwhich might usefully be separated to
@
\deq -> let eq = eqFromEqDict deq in \xy -> ...
@
Well, maybe. We don't do this at the moment.
\begin{code}
-type LevelledExpr = GenCoreExpr (Id, Level) Id TyVar UVar
-type LevelledBind = GenCoreBinding (Id, Level) Id TyVar UVar
-type FloatingBind = (Level, Floater)
-type FloatingBinds = [FloatingBind]
-
-data Floater
- = LetFloater CoreBinding
- | CaseFloater (CoreExpr -> CoreExpr)
- -- A CoreExpr with a hole in it:
- -- "Give me a right-hand side of the
- -- (usually single) alternative, and
- -- I'll build the case..."
+type LevelledExpr = TaggedExpr Level
+type LevelledBind = TaggedBind Level
+type FloatBind = (Level, CoreBind)
+type FloatBinds = [FloatBind]
\end{code}
%************************************************************************
%************************************************************************
\begin{code}
-floatOutwards :: UniqSupply -> [CoreBinding] -> [CoreBinding]
-
-floatOutwards us pgm
- = case (setLevels pgm us) of { annotated_w_levels ->
-
- case (unzip (map floatTopBind annotated_w_levels))
- of { (fss, final_toplev_binds_s) ->
-
- (if opt_D_verbose_core2core
- then pprTrace "Levels added:\n"
- (ppAboves (map (ppr PprDebug) annotated_w_levels))
- else id
- )
- ( if not (opt_D_simplifier_stats) then
- id
- else
- let
- (tlets, ntlets, lams) = get_stats (sum_stats fss)
- in
- pprTrace "FloatOut stats: " (ppBesides [
- ppInt tlets, ppStr " Lets floated to top level; ",
- ppInt ntlets, ppStr " Lets floated elsewhere; from ",
- ppInt lams, ppStr " Lambda groups"])
- )
- concat final_toplev_binds_s
- }}
+floatOutwards :: DynFlags
+ -> Bool -- True <=> float lambdas to top level
+ -> UniqSupply
+ -> [CoreBind] -> IO [CoreBind]
+
+floatOutwards dflags float_lams us pgm
+ = do {
+ showPass dflags float_msg ;
+
+ let { annotated_w_levels = setLevels float_lams pgm us ;
+ (fss, binds_s') = unzip (map floatTopBind annotated_w_levels)
+ } ;
+
+ dumpIfSet_dyn dflags Opt_D_verbose_core2core "Levels added:"
+ (vcat (map ppr annotated_w_levels));
+
+ let { (tlets, ntlets, lams) = get_stats (sum_stats fss) };
+
+ dumpIfSet_dyn dflags Opt_D_dump_simpl_stats "FloatOut stats:"
+ (hcat [ int tlets, ptext SLIT(" Lets floated to top level; "),
+ int ntlets, ptext SLIT(" Lets floated elsewhere; from "),
+ int lams, ptext SLIT(" Lambda groups")]);
+
+ endPass dflags float_msg Opt_D_verbose_core2core (concat binds_s')
+ {- no specific flag for dumping float-out -}
+ }
+ where
+ float_msg | float_lams = "Float out (floating lambdas too)"
+ | otherwise = "Float out (not floating lambdas)"
floatTopBind bind@(NonRec _ _)
- = case (floatBind nullIdEnv tOP_LEVEL bind) of { (fs, floats, bind', _) ->
+ = case (floatBind emptyVarEnv tOP_LEVEL bind) of { (fs, floats, bind', _) ->
(fs, floatsToBinds floats ++ [bind'])
}
floatTopBind bind@(Rec _)
- = case (floatBind nullIdEnv tOP_LEVEL bind) of { (fs, floats, Rec pairs', _) ->
+ = case (floatBind emptyVarEnv tOP_LEVEL bind) of { (fs, floats, Rec pairs', _) ->
-- Actually floats will be empty
--false:ASSERT(null floats)
(fs, [Rec (floatsToBindPairs floats ++ pairs')])
floatBind :: IdEnv Level
-> Level
-> LevelledBind
- -> (FloatStats, FloatingBinds, CoreBinding, IdEnv Level)
+ -> (FloatStats, FloatBinds, CoreBind, IdEnv Level)
floatBind env lvl (NonRec (name,level) rhs)
- = case (floatExpr env level rhs) of { (fs, rhs_floats, rhs') ->
-
- -- A good dumping point
- case (partitionByMajorLevel level rhs_floats) of { (rhs_floats', heres) ->
-
- (fs, rhs_floats',
- NonRec name (install heres rhs'),
- addOneToIdEnv env name level)
- }}
+ = case (floatRhs env level rhs) of { (fs, rhs_floats, rhs') ->
+ (fs, rhs_floats,
+ NonRec name rhs',
+ extendVarEnv env name level)
+ }
floatBind env lvl bind@(Rec pairs)
= case (unzip3 (map do_pair pairs)) of { (fss, rhss_floats, new_pairs) ->
}
where
- new_env = growIdEnvList env (map fst pairs)
+ new_env = extendVarEnvList env (map fst pairs)
bind_level = getBindLevel bind
do_pair ((name, level), rhs)
- = case (floatExpr new_env level rhs) of { (fs, rhs_floats, rhs') ->
-
- -- A good dumping point
- case (partitionByMajorLevel level rhs_floats) of { (rhs_floats', heres) ->
-
- (fs, rhs_floats', (name, install heres rhs'))
- }}
+ = case (floatRhs new_env level rhs) of { (fs, rhs_floats, rhs') ->
+ (fs, rhs_floats, (name, rhs'))
+ }
\end{code}
%************************************************************************
%************************************************************************
\begin{code}
-floatExpr :: IdEnv Level
- -> Level
- -> LevelledExpr
- -> (FloatStats, FloatingBinds, CoreExpr)
-
-floatExpr env _ (Var v) = (zero_stats, [], Var v)
-floatExpr env _ (Lit l) = (zero_stats, [], Lit l)
-floatExpr env _ (Prim op as) = (zero_stats, [], Prim op as)
-floatExpr env _ (Con con as) = (zero_stats, [], Con con as)
+floatExpr, floatRhs
+ :: IdEnv Level
+ -> Level
+ -> LevelledExpr
+ -> (FloatStats, FloatBinds, CoreExpr)
+
+floatRhs env lvl arg
+ = case (floatExpr env lvl arg) of { (fsa, floats, arg') ->
+ case (partitionByMajorLevel lvl floats) of { (floats', heres) ->
+ -- Dump bindings that aren't going to escape from a lambda
+ -- This is to avoid floating the x binding out of
+ -- f (let x = e in b)
+ -- unnecessarily. It even causes a bug to do so if we have
+ -- y = writeArr# a n (let x = e in b)
+ -- because the y binding is an expr-ok-for-speculation one.
+ (fsa, floats', install heres arg') }}
+
+floatExpr env _ (Var v) = (zeroStats, [], Var v)
+floatExpr env _ (Type ty) = (zeroStats, [], Type ty)
+floatExpr env _ (Lit lit) = (zeroStats, [], Lit lit)
floatExpr env lvl (App e a)
- = case (floatExpr env lvl e) of { (fs, floating_defns, e') ->
- (fs, floating_defns, App e' a) }
+ = case (floatExpr env lvl e) of { (fse, floats_e, e') ->
+ case (floatRhs env lvl a) of { (fsa, floats_a, a') ->
+ (fse `add_stats` fsa, floats_e ++ floats_a, App e' a') }}
-floatExpr env lvl (Lam (UsageBinder _) e)
- = panic "FloatOut.floatExpr: Lam UsageBinder"
-
-floatExpr env lvl (Lam (TyBinder tv) e)
- = let
- incd_lvl = incMinorLvl lvl
- in
- case (floatExpr env incd_lvl e) of { (fs, floats, e') ->
+floatExpr env lvl (Lam (tv,incd_lvl) e)
+ | isTyVar tv
+ = case (floatExpr env incd_lvl e) of { (fs, floats, e') ->
-- Dump any bindings which absolutely cannot go any further
case (partitionByLevel incd_lvl floats) of { (floats', heres) ->
- (fs, floats', Lam (TyBinder tv) (install heres e'))
+ (fs, floats', Lam tv (install heres e'))
}}
-floatExpr env lvl (Lam (ValBinder (arg,incd_lvl)) rhs)
- = let
- new_env = addOneToIdEnv env arg incd_lvl
+floatExpr env lvl (Lam (arg,incd_lvl) rhs)
+ = ASSERT( isId arg )
+ let
+ new_env = extendVarEnv env arg incd_lvl
in
case (floatExpr new_env incd_lvl rhs) of { (fs, floats, rhs') ->
(add_to_stats fs floats',
floats',
- Lam (ValBinder arg) (install heres rhs'))
+ Lam arg (install heres rhs'))
}}
-floatExpr env lvl (SCC cc expr)
+floatExpr env lvl (Note note@(SCC cc) expr)
= case (floatExpr env lvl expr) of { (fs, floating_defns, expr') ->
let
- -- annotate bindings floated outwards past an scc expression
+ -- Annotate bindings floated outwards past an scc expression
-- with the cc. We mark that cc as "duplicated", though.
annotated_defns = annotate (dupifyCC cc) floating_defns
in
- (fs, annotated_defns, SCC cc expr') }
+ (fs, annotated_defns, Note note expr') }
where
- annotate :: CostCentre -> FloatingBinds -> FloatingBinds
+ annotate :: CostCentre -> FloatBinds -> FloatBinds
annotate dupd_cc defn_groups
= [ (level, ann_bind floater) | (level, floater) <- defn_groups ]
where
- ann_bind (LetFloater (NonRec binder rhs))
- = LetFloater (NonRec binder (ann_rhs rhs))
-
- ann_bind (LetFloater (Rec pairs))
- = LetFloater (Rec [(binder, ann_rhs rhs) | (binder, rhs) <- pairs])
-
- ann_bind (CaseFloater fn) = CaseFloater ( \ rhs -> SCC dupd_cc (fn rhs) )
-
- ann_rhs (Lam arg e) = Lam arg (ann_rhs e)
- ann_rhs rhs@(Con _ _) = rhs -- no point in scc'ing WHNF data
- ann_rhs rhs = SCC dupd_cc rhs
-
- -- Note: Nested SCC's are preserved for the benefit of
- -- cost centre stack profiling (Durham)
-
-floatExpr env lvl (Coerce c ty expr)
+ ann_bind (NonRec binder rhs)
+ = NonRec binder (mkSCC dupd_cc rhs)
+
+ ann_bind (Rec pairs)
+ = Rec [(binder, mkSCC dupd_cc rhs) | (binder, rhs) <- pairs]
+
+-- At one time I tried the effect of not float anything out of an InlineMe,
+-- but it sometimes works badly. For example, consider PrelArr.done. It
+-- has the form __inline (\d. e)
+-- where e doesn't mention d. If we float this to
+-- __inline (let x = e in \d. x)
+-- things are bad. The inliner doesn't even inline it because it doesn't look
+-- like a head-normal form. So it seems a lesser evil to let things float.
+-- In SetLevels we do set the context to (Level 0 0) when we get to an InlineMe
+-- which discourages floating out.
+
+floatExpr env lvl (Note note expr) -- Other than SCCs
= case (floatExpr env lvl expr) of { (fs, floating_defns, expr') ->
- (fs, floating_defns, Coerce c ty expr') }
+ (fs, floating_defns, Note note expr') }
floatExpr env lvl (Let bind body)
= case (floatBind env lvl bind) of { (fsb, rhs_floats, bind', new_env) ->
case (floatExpr new_env lvl body) of { (fse, body_floats, body') ->
(add_stats fsb fse,
- rhs_floats ++ [(bind_lvl, LetFloater bind')] ++ body_floats,
+ rhs_floats ++ [(bind_lvl, bind')] ++ body_floats,
body')
}}
where
bind_lvl = getBindLevel bind
-floatExpr env lvl (Case scrut alts)
- = case (floatExpr env lvl scrut) of { (fse, fde, scrut') ->
-
- case (scrut', float_alts alts) of
- (_, (fsa, fda, alts')) ->
- (add_stats fse fsa, fda ++ fde, Case scrut' alts')
- }
- {- OLD CASE-FLOATING CODE: DROPPED FOR NOW. (SLPJ 7/2/94)
-
- (Var scrut_var, (fda, AlgAlts [(con,bs,rhs')] NoDefault))
- | scrut_var_lvl `ltMajLvl` lvl ->
-
- -- Candidate for case floater; scrutinising a variable; it can
- -- escape outside a lambda; there's only one alternative.
- (fda ++ fde ++ [case_floater], rhs')
-
- where
- case_floater = (scrut_var_lvl, CaseFloater fn)
- fn body = Case scrut' (AlgAlts [(con,bs,body)] NoDefault)
- scrut_var_lvl = case lookupIdEnv env scrut_var of
- Nothing -> Level 0 0
- Just lvl -> unTopify lvl
-
- END OF CASE FLOATING DROPPED -}
+floatExpr env lvl (Case scrut (case_bndr, case_lvl) alts)
+ = case floatExpr env lvl scrut of { (fse, fde, scrut') ->
+ case floatList float_alt alts of { (fsa, fda, alts') ->
+ (add_stats fse fsa, fda ++ fde, Case scrut' case_bndr alts')
+ }}
where
- incd_lvl = incMinorLvl lvl
+ alts_env = extendVarEnv env case_bndr case_lvl
partition_fn = partitionByMajorLevel
-{- OMITTED
- We don't want to be too keen about floating lets out of case alternatives
- because they may benefit from seeing the evaluation done by the case.
-
- The main reason for doing this is to allocate in fewer larger blocks
- but that's really an STG-level issue.
-
- case alts of
- -- Just one alternative, then dump only
- -- what *has* to be dumped
- AlgAlts [_] NoDefault -> partitionByLevel
- AlgAlts [] (BindDefault _ _) -> partitionByLevel
- PrimAlts [_] NoDefault -> partitionByLevel
- PrimAlts [] (BindDefault _ _) -> partitionByLevel
-
- -- If there's more than one alternative, then
- -- this is a dumping point
- other -> partitionByMajorLevel
--}
-
- float_alts (AlgAlts alts deflt)
- = case (float_deflt deflt) of { (fsd, fdd, deflt') ->
- case (unzip3 (map float_alg_alt alts)) of { (fsas, fdas, alts') ->
- (foldr add_stats fsd fsas,
- concat fdas ++ fdd,
- AlgAlts alts' deflt') }}
-
- float_alts (PrimAlts alts deflt)
- = case (float_deflt deflt) of { (fsd, fdd, deflt') ->
- case (unzip3 (map float_prim_alt alts)) of { (fsas, fdas, alts') ->
- (foldr add_stats fsd fsas,
- concat fdas ++ fdd,
- PrimAlts alts' deflt') }}
-
- -------------
- float_alg_alt (con, bs, rhs)
+ float_alt (con, bs, rhs)
= let
bs' = map fst bs
- new_env = growIdEnvList env bs
+ new_env = extendVarEnvList alts_env bs
in
- case (floatExpr new_env incd_lvl rhs) of { (fs, rhs_floats, rhs') ->
- case (partition_fn incd_lvl rhs_floats) of { (rhs_floats', heres) ->
+ case (floatExpr new_env case_lvl rhs) of { (fs, rhs_floats, rhs') ->
+ case (partition_fn case_lvl rhs_floats) of { (rhs_floats', heres) ->
(fs, rhs_floats', (con, bs', install heres rhs')) }}
- --------------
- float_prim_alt (lit, rhs)
- = case (floatExpr env incd_lvl rhs) of { (fs, rhs_floats, rhs') ->
- case (partition_fn incd_lvl rhs_floats) of { (rhs_floats', heres) ->
- (fs, rhs_floats', (lit, install heres rhs')) }}
-
- --------------
- float_deflt NoDefault = (zero_stats, [], NoDefault)
-
- float_deflt (BindDefault (b,lvl) rhs)
- = case (floatExpr new_env lvl rhs) of { (fs, rhs_floats, rhs') ->
- case (partition_fn incd_lvl rhs_floats) of { (rhs_floats', heres) ->
- (fs, rhs_floats', BindDefault b (install heres rhs')) }}
- where
- new_env = addOneToIdEnv env b lvl
+
+floatList :: (a -> (FloatStats, FloatBinds, b)) -> [a] -> (FloatStats, FloatBinds, [b])
+floatList f [] = (zeroStats, [], [])
+floatList f (a:as) = case f a of { (fs_a, binds_a, b) ->
+ case floatList f as of { (fs_as, binds_as, bs) ->
+ (fs_a `add_stats` fs_as, binds_a ++ binds_as, b:bs) }}
\end{code}
%************************************************************************
get_stats (FlS a b c) = (a, b, c)
-zero_stats = FlS 0 0 0
+zeroStats = FlS 0 0 0
-sum_stats xs = foldr add_stats zero_stats xs
+sum_stats xs = foldr add_stats zeroStats xs
add_stats (FlS a1 b1 c1) (FlS a2 b2 c2)
= FlS (a1 + a2) (b1 + b2) (c1 + c2)
to_very_top (my_lvl, _) = isTopLvl my_lvl
\end{code}
+
%************************************************************************
%* *
\subsection{Utility bits for floating}
partitionByMajorLevel, partitionByLevel
:: Level -- Partitioning level
- -> FloatingBinds -- Defns to be divided into 2 piles...
+ -> FloatBinds -- Defns to be divided into 2 piles...
- -> (FloatingBinds, -- Defns with level strictly < partition level,
- FloatingBinds) -- The rest
+ -> (FloatBinds, -- Defns with level strictly < partition level,
+ FloatBinds) -- The rest
partitionByMajorLevel ctxt_lvl defns
= partition float_further defns
where
- float_further (my_lvl, _) = my_lvl `ltMajLvl` ctxt_lvl ||
- isTopLvl my_lvl
+ -- Float it if we escape a value lambda,
+ -- or if we get to the top level
+ float_further (my_lvl, bind) = my_lvl `ltMajLvl` ctxt_lvl || isTopLvl my_lvl
+ -- The isTopLvl part says that if we can get to the top level, say "yes" anyway
+ -- This means that
+ -- x = f e
+ -- transforms to
+ -- lvl = e
+ -- x = f lvl
+ -- which is as it should be
partitionByLevel ctxt_lvl defns
= partition float_further defns
\end{code}
\begin{code}
-floatsToBinds :: FloatingBinds -> [CoreBinding]
-floatsToBinds floats = map get_bind floats
- where
- get_bind (_, LetFloater bind) = bind
- get_bind (_, CaseFloater _) = panic "floatsToBinds"
+floatsToBinds :: FloatBinds -> [CoreBind]
+floatsToBinds floats = map snd floats
-floatsToBindPairs :: FloatingBinds -> [(Id,CoreExpr)]
+floatsToBindPairs :: FloatBinds -> [(Id,CoreExpr)]
floatsToBindPairs floats = concat (map mk_pairs floats)
where
- mk_pairs (_, LetFloater (Rec pairs)) = pairs
- mk_pairs (_, LetFloater (NonRec binder rhs)) = [(binder,rhs)]
- mk_pairs (_, CaseFloater _) = panic "floatsToBindPairs"
+ mk_pairs (_, Rec pairs) = pairs
+ mk_pairs (_, NonRec binder rhs) = [(binder,rhs)]
-install :: FloatingBinds -> CoreExpr -> CoreExpr
+install :: FloatBinds -> CoreExpr -> CoreExpr
install defn_groups expr
= foldr install_group expr defn_groups
where
- install_group (_, LetFloater defns) body = Let defns body
- install_group (_, CaseFloater fn) body = fn body
+ install_group (_, defns) body = Let defns body
\end{code}