summaryrefslogtreecommitdiff
path: root/compiler/GHC/Core/Opt/SetLevels.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Core/Opt/SetLevels.hs')
-rw-r--r--compiler/GHC/Core/Opt/SetLevels.hs103
1 files changed, 60 insertions, 43 deletions
diff --git a/compiler/GHC/Core/Opt/SetLevels.hs b/compiler/GHC/Core/Opt/SetLevels.hs
index 2fdd5ba362..95084cf7b6 100644
--- a/compiler/GHC/Core/Opt/SetLevels.hs
+++ b/compiler/GHC/Core/Opt/SetLevels.hs
@@ -685,15 +685,16 @@ lvlMFE env strict_ctxt ann_expr
expr_ty = exprType expr
fvs = freeVarsOf ann_expr
fvs_ty = tyCoVarsOfType expr_ty
- is_bot = isBottomThunk mb_bot_str
- is_bot_lam = isJust mb_bot_str
+ is_bot_lam = isJust mb_bot_str -- True of bottoming thunks too!
is_function = isFunction ann_expr
mb_bot_str = exprBotStrictness_maybe expr
-- See Note [Bottoming floats]
-- esp Bottoming floats (2)
expr_ok_for_spec = exprOkForSpeculation expr
- dest_lvl = destLevel env fvs fvs_ty is_function is_bot False
- abs_vars = abstractVars dest_lvl env fvs
+ abs_vars = abstractVars dest_lvl env fvs
+ dest_lvl = destLevel env fvs fvs_ty is_function is_bot_lam False
+ -- NB: is_bot_lam not is_bot; see (3) in
+ -- Note [Bottoming floats]
-- float_is_new_lam: the floated thing will be a new value lambda
-- replacing, say (g (x+4)) by (lvl x). No work is saved, nor is
@@ -725,7 +726,9 @@ lvlMFE env strict_ctxt ann_expr
-- See Note [Floating to the top]
saves_alloc = isTopLvl dest_lvl
&& floatConsts env
- && (not strict_ctxt || is_bot || exprIsHNF expr)
+ && ( not strict_ctxt -- (a)
+ || exprIsHNF expr -- (b)
+ || (is_bot_lam && escapes_value_lam)) -- (c)
hasFreeJoin :: LevelEnv -> DVarSet -> Bool
-- Has a free join point which is not being floated to top level.
@@ -735,55 +738,63 @@ hasFreeJoin :: LevelEnv -> DVarSet -> Bool
hasFreeJoin env fvs
= not (maxFvLevel isJoinId env fvs == tOP_LEVEL)
-isBottomThunk :: Maybe (Arity, DmdSig, CprSig) -> Bool
--- See Note [Bottoming floats] (2)
-isBottomThunk (Just (0, _, _)) = True -- Zero arity
-isBottomThunk _ = False
-
{- Note [Floating to the top]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We are keen to float something to the top level, even if it does not
-escape a value lambda (and hence save work), for two reasons:
-
- * Doing so makes the function smaller, by floating out
- bottoming expressions, or integer or string literals. That in
- turn makes it easier to inline, with less duplication.
-
- * (Minor) Doing so may turn a dynamic allocation (done by machine
- instructions) into a static one. Minor because we are assuming
- we are not escaping a value lambda.
-
-But do not do so if (saves_alloc):
- - the context is strict, and
- - the expression is not a HNF, and
- - the expression is not bottoming
+Suppose saves_work is False, i.e.
+ - 'e' does not escape a value lambda (escapes_value_lam), or
+ - 'e' would have added value lambdas if floated (float_is_new_lam)
+Then we may still be keen to float a sub-expression 'e' to the top level,
+for two reasons:
+
+ (i) Doing so makes the function smaller, by floating out
+ bottoming expressions, or integer or string literals. That in
+ turn makes it easier to inline, with less duplication.
+ This only matters if the floated sub-expression is inside a
+ value-lambda, which in turn may be easier to inline.
+
+ (ii) (Minor) Doing so may turn a dynamic allocation (done by machine
+ instructions) into a static one. Minor because we are assuming
+ we are not escaping a value lambda.
+
+But only do so if (saves_alloc):
+ (a) the context is lazy (so we get allocation), or
+ (b) the expression is a HNF (so we get allocation), or
+ (c) the expression is bottoming and (i) applies
+ (NB: if the expression is a lambda, (b) will apply;
+ so this case only catches bottoming thunks)
Examples:
-* Bottoming
- f x = case x of
- 0 -> error <big thing>
- _ -> x+1
- Here we want to float (error <big thing>) to top level, abstracting
- over 'x', so as to make f's RHS smaller.
-
-* HNF
- f = case y of
- True -> p:q
- False -> blah
- We may as well float the (p:q) so it becomes a static data structure.
-
-* Case scrutinee
+* (a) Strict. Case scrutinee
f = case g True of ....
Don't float (g True) to top level; then we have the admin of a
top-level thunk to worry about, with zero gain.
-* Case alternative
+* (a) Strict. Case alternative
h = case y of
True -> g True
False -> False
Don't float (g True) to the top level
+* (b) HNF
+ f = case y of
+ True -> p:q
+ False -> blah
+ We may as well float the (p:q) so it becomes a static data structure.
+
+* (c) Bottoming expressions; see also Note [Bottoming floats]
+ f x = case x of
+ 0 -> error <big thing>
+ _ -> x+1
+ Here we want to float (error <big thing>) to top level, abstracting
+ over 'x', so as to make f's RHS smaller.
+
+ But (#22494) if it's more like
+ foo = case error <thing> of { ... }
+ then there is no point in floating; we are never going to inline
+ 'foo' anyway. So float bottoming things only if they escape
+ a lambda.
+
* Arguments
t = f (g True)
Prior to Apr 22 we didn't float (g True) to the top if f was strict.
@@ -912,7 +923,7 @@ But, as ever, we need to be careful:
(1) 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 abstract over 'x' can float the whole arg of g:
+ Then we'd like to abstract over 'x', and float the whole arg of g:
lvl = \x. let v = h x in error ("urk" ++ v)
f = \x. g (lvl x)
To achieve this we pass is_bot to destLevel
@@ -921,6 +932,12 @@ But, as ever, we need to be careful:
bottom. Instead we treat the /body/ of such a function specially,
via point (1). For example:
f = \x. ....(\y z. if x then error y else error z)....
+ If we float the whole lambda thus
+ lvl = \x. \y z. if x then error y else error z
+ f = \x. ...(lvl x)...
+ we may well end up eta-expanding that PAP to
+ f = \x. ...(\y z. lvl x y z)...
+
===>
lvl = \x z y. if b then error y else error z
f = \x. ...(\y z. lvl x z y)...
@@ -1402,7 +1419,7 @@ destLevel :: LevelEnv
-> TyCoVarSet -- Free in the /type/ of the term
-- (a subset of the previous argument)
-> Bool -- True <=> is function
- -> Bool -- True <=> is bottom
+ -> Bool -- True <=> looks like \x1..xn.bottom (n>=0)
-> Bool -- True <=> is a join point
-> Level
-- INVARIANT: if is_join=True then result >= join_ceiling
@@ -1419,7 +1436,7 @@ destLevel env fvs fvs_ty is_function is_bot is_join
| is_bot -- Send bottoming bindings to the top
= as_far_as_poss -- regardless; see Note [Bottoming floats]
- -- Esp Bottoming floats (1)
+ -- Esp Bottoming floats (1) and (3)
| Just n_args <- floatLams env
, n_args > 0 -- n=0 case handled uniformly by the 'otherwise' case