summaryrefslogtreecommitdiff
path: root/compiler/GHC/Core/Utils.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Core/Utils.hs')
-rw-r--r--compiler/GHC/Core/Utils.hs376
1 files changed, 3 insertions, 373 deletions
diff --git a/compiler/GHC/Core/Utils.hs b/compiler/GHC/Core/Utils.hs
index 2d287a1b3d..87dc238d62 100644
--- a/compiler/GHC/Core/Utils.hs
+++ b/compiler/GHC/Core/Utils.hs
@@ -37,9 +37,6 @@ module GHC.Core.Utils (
cheapEqExpr, cheapEqExpr', eqExpr,
diffBinds,
- -- * Lambdas and eta reduction
- tryEtaReduce, canEtaReduceToArity,
-
-- * Manipulating data constructors and types
exprToType,
applyTypeToArgs,
@@ -71,11 +68,9 @@ import GHC.Platform
import GHC.Core
import GHC.Core.Ppr
-import GHC.Core.FVs( exprFreeVars )
import GHC.Core.DataCon
import GHC.Core.Type as Type
import GHC.Core.FamInstEnv
-import GHC.Core.Predicate
import GHC.Core.TyCo.Rep( TyCoBinder(..), TyBinder )
import GHC.Core.Coercion
import GHC.Core.Reduction
@@ -95,10 +90,11 @@ import GHC.Types.Literal
import GHC.Types.Tickish
import GHC.Types.Id
import GHC.Types.Id.Info
+import GHC.Types.Basic( Arity, Levity(..)
+ , CbvMark(..), isMarkedCbv )
import GHC.Types.Unique
-import GHC.Types.Basic
-import GHC.Types.Demand
import GHC.Types.Unique.Set
+import GHC.Types.Demand
import GHC.Data.FastString
import GHC.Data.Maybe
@@ -2326,372 +2322,6 @@ locBind loc b1 b2 diffs = map addLoc diffs
bindLoc | b1 == b2 = ppr b1
| otherwise = ppr b1 <> char '/' <> ppr b2
-{-
-************************************************************************
-* *
- Eta reduction
-* *
-************************************************************************
-
-Note [Eta reduction makes sense]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Traditionally, eta reduction transforms `\x. e x` to `e`, where `e` is an
-arbitrary expression in which `x` doesn't occur free.
-It is the inverse of eta expansion, which generally transforms the program into
-a form that executes faster. So why and when will GHC attempt to eta *reduce*?
-
-1. We want to eta-reduce only if we get all the way to a
- trivial expression; we don't want to remove extra lambdas unless we are going
- to avoid allocating this thing altogether.
- Trivial means *including* casts and type lambdas:
- * `\x. f x |> co --> f |> (ty(x) -> co)` (provided `co` doesn't mention `x`)
- * `/\a. \x. f @(Maybe a) x --> /\a. f @(Maybe a)`
-
-2. It's always sound to eta-reduce *type* lambdas and we always want to, because
- it makes RULEs apply more often:
- This RULE: `forall g. foldr (build (/\a -> g a))`
- should match `foldr (build (/\b -> ...something complex...))`
- and the simplest way to do so is eta-reduce `/\a -> g a` in the RULE to `g`.
- The type checker can insert these eta-expanded versions of the RULE.
- [SG: This is implied by (1), isn't it? Perhaps we want to eta-reduce type
- lambdas even if the resulting expression is non-trivial?]
-
-3. We have to hide `f`'s `idArity` in its own RHS, lest we suffer from the last
- point of Note [Arity robustness]. There we have `f = \x. f x` and we should
- not eta-reduce to `f=f`. Which might change a terminating program
- (think @f `seq` e@) to a non-terminating one.
- So we check for being a loop breaker first. However for GlobalIds we can look
- at the arity; and for primops we must, since they have no unfolding.
- [SG: Perhaps this is rather a soundness subtlety?]
-
-Of course, eta reduction is not always sound. See Note [Eta reduction soundness]
-for when it is.
-
-When there are multiple arguments, we might get multiple eta-redexes. Example:
- \x y. e x y
- ==> { reduce \y. (e x) y in context \x._ }
- \x. e x
- ==> { reduce \x. e x in context _ }
- e
-And (1) implies that we never want to stop with `\x. e x`, because that is not a
-trivial expression. So in practice, the implementation works by considering a
-whole group of leading lambdas to reduce.
-
-These delicacies are why we don't simply use 'exprIsTrivial' and 'exprIsHNF'
-in 'tryEtaReduce'. Alas.
-
-Note [Eta reduction soundness]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-As Note [Eta reduction makes sense] explains, GHC's eta reduction transforms
-`\x y. e x y` to `e`, where `e` is an arbitrary expression in which `x` and `y`
-don't occur free.
-
-Eta reduction is *not* a sound transformation in general, because it
-may change termination behavior if *value* lambdas are involved:
- `bot` /= `\x. bot x` (as can be observed by a simple `seq`)
-The past has shown that oversight of this fact can not only lead to endless
-loops or exceptions, but also straight out *segfaults*.
-
-Nevertheless, we can give the following criteria for when it is sound to
-perform eta reduction on an expression with n leading lambdas `\xs. e xs`
-(checked in 'is_eta_reduction_sound' in 'tryEtaReduce', which focuses on the
-case where `e` is trivial):
-
- A. It is sound to eta-reduce n arguments as long as n does not exceed the
- `exprArity` of `e`. (Needs Arity analysis.)
- This criterion exploits information about how `e` is *defined*.
-
- Example: If `e = \x. bot` then we know it won't diverge until it is called
- with one argument. Hence it is safe to eta-reduce `\x. e x` to `e`.
- By contrast, it would be *unsound* to eta-reduce 2 args, `\x y. e x y` to `e`:
- `e 42` diverges when `(\x y. e x y) 42` does not.
-
- S. It is sound to eta-reduce n arguments in an evaluation context in which all
- calls happen with at least n arguments. (Needs Strictness analysis.)
- NB: This treats evaluations like a call with 0 args.
- NB: This criterion exploits information about how `e` is *used*.
-
- Example: Given a function `g` like
- `g c = Just (c 1 2 + c 2 3)`
- it is safe to eta-reduce the arg in `g (\x y. e x y)` to `g e` without
- knowing *anything* about `e` (perhaps it's a parameter occ itself), simply
- because `g` always calls its parameter with 2 arguments.
- It is also safe to eta-reduce just one arg, e.g., `g (\x. e x)` to `g e`.
- By contrast, it would *unsound* to eta-reduce 3 args in a call site
- like `g (\x y z. e x y z)` to `g e`, because that diverges when
- `e = \x y. bot`.
-
- Could we relax to "*At least one call in the same trace* is with n args"?
- (NB: Strictness analysis can only answer this relaxed question, not the
- original formulation.)
- Consider what happens for
- ``g2 c = c True `seq` c False 42``
- Here, `g2` will call `c` with 2 arguments (if there is a call at all).
- But it is unsound to eta-reduce the arg in `g2 (\x y. e x y)` to `g2 e`
- when `e = \x. if x then bot else id`, because the latter will diverge when
- the former would not.
- On the other hand, with `-fno-pendantic-bottoms` , we will have eta-expanded
- the definition of `e` and then eta-reduction is sound
- (see Note [Dealing with bottom]).
- Consequence: We have to check that `-fpedantic-bottoms` is off; otherwise
- eta-reduction based on demands is in fact unsound.
-
- See Note [Eta reduction based on evaluation context] for the implementation
- details. This criterion is tested extensively in T21261.
-
- E. As a perhaps special case on the boundary of (A) and (S), when we know that
- a fun binder `f` is in WHNF, we simply assume it has arity 1 and apply (A).
- Example:
- ``g f = f `seq` \x. f x``
- Here it's sound eta-reduce `\x. f x` to `f`, because `f` can't be bottom
- after the `seq`. This turned up in #7542.
-
-And here are a few more technical criteria for when it is *not* sound to
-eta-reduce that are specific to Core and GHC:
-
- L. With linear types, eta-reduction can break type-checking:
- f :: A ⊸ B
- g :: A -> B
- g = \x. f x
- The above is correct, but eta-reducing g would yield g=f, the linter will
- complain that g and f don't have the same type. NB: Not unsound in the
- dynamic semantics, but unsound according to the static semantics of Core.
-
- J. We may not undersaturate join points.
- See Note [Invariants on join points] in GHC.Core, and #20599.
-
- B. We may not undersaturate functions with no binding.
- See Note [Eta expanding primops].
-
- W. We may not undersaturate StrictWorkerIds.
- See Note [Strict Worker Ids] in GHC.CoreToStg.Prep.
-
-Here is a list of historic accidents surrounding unsound eta-reduction:
-
-* Consider
- f = \x.f x
- h y = case (case y of { True -> f `seq` True; False -> False }) of
- True -> ...; False -> ...
- If we (unsoundly) eta-reduce f to get f=f, the strictness analyser
- says f=bottom, and replaces the (f `seq` True) with just
- (f `cast` unsafe-co).
- [SG in 2022: I don't think worker/wrapper would do this today.]
- BUT, as things stand, 'f' got arity 1, and it *keeps* arity 1 (perhaps also
- wrongly). So CorePrep eta-expands the definition again, so that it does not
- terminate after all.
- Result: seg-fault because the boolean case actually gets a function value.
- See #1947.
-
-* Never *reduce* arity. For example
- f = \xy. g x y
- Then if h has arity 1 we don't want to eta-reduce because then
- f's arity would decrease, and that is bad
- [SG in 2022: I don't understand this point. There is no `h`, perhaps that
- should have been `g`. Even then, this proposed eta-reduction is invalid by
- criterion (A), which might actually be the point this anecdote is trying to
- make. Perhaps the "no arity decrease" idea is also related to
- Note [Arity robustness]?]
-
-Note [Eta reduction with casted arguments]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Consider
- (\(x:t3). f (x |> g)) :: t3 -> t2
- where
- f :: t1 -> t2
- g :: t3 ~ t1
-This should be eta-reduced to
-
- f |> (sym g -> t2)
-
-So we need to accumulate a coercion, pushing it inward (past
-variable arguments only) thus:
- f (x |> co_arg) |> co --> (f |> (sym co_arg -> co)) x
- f (x:t) |> co --> (f |> (t -> co)) x
- f @ a |> co --> (f |> (forall a.co)) @ a
- f @ (g:t1~t2) |> co --> (f |> (t1~t2 => co)) @ (g:t1~t2)
-These are the equations for ok_arg.
-
-Note [Eta reduction with casted function]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Since we are pushing a coercion inwards, it is easy to accommodate
- (\xy. (f x |> g) y)
- (\xy. (f x y) |> g)
-
-See the `(Cast e co)` equation for `go` in `tryEtaReduce`. The
-eta-expander pushes those casts outwards, so you might think we won't
-ever see a cast here, but if we have
- \xy. (f x y |> g)
-we will call tryEtaReduce [x,y] (f x y |> g), and we'd like that to
-work. This happens in GHC.Core.Opt.Simplify.Utils.mkLam, where
-eta-expansion may be turned off (by sm_eta_expand).
-
-Note [Eta reduction based on evaluation context]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Note [Eta reduction soundness], criterion (S) allows us to eta-reduce
-`g (\x y. e x y)` to `g e` when we know that `g` always calls its parameter with
-at least 2 arguments. So how do we read that off `g`'s demand signature?
-
-Let's take the simple example of #21261, where `g` (actually, `f`) is defined as
- g c = c 1 2 + c 3 4
-Then this is how the pieces are put together:
-
- * Demand analysis infers `<SCS(C1(L))>` for `g`'s demand signature
-
- * When the Simplifier next simplifies the argument in `g (\x y. e x y)`, it
- looks up the *evaluation context* of the argument in the form of the
- sub-demand `CS(C1(L))` and stores it in the 'SimplCont'.
- (Why does it drop the outer evaluation cardinality of the demand, `S`?
- Because it's irrelevant! When we simplify an expression, we do so under the
- assumption that it is currently under evaluation.)
- This sub-demand literally says "Whenever this expression is evaluated, it
- is also called with two arguments, potentially multiple times".
-
- * Then the simplifier takes apart the lambda and simplifies the lambda group
- and then calls 'tryEtaReduce' when rebuilding the lambda, passing the
- evaluation context `CS(C1(L))` along. Then we simply peel off 2 call
- sub-demands `Cn` and see whether all of the n's (here: `S=C_1N` and
- `1=C_11`) were strict. And strict they are! Thus, it will eta-reduce
- `\x y. e x y` to `e`.
--}
-
--- | `tryEtaReduce [x,y,z] e sd` returns `Just e'` if `\x y z -> e` is evaluated
--- according to `sd` and can soundly and gainfully be eta-reduced to `e'`.
--- See Note [Eta reduction soundness]
--- and Note [Eta reduction makes sense] when that is the case.
-tryEtaReduce :: [Var] -> CoreExpr -> SubDemand -> Maybe CoreExpr
--- Return an expression equal to (\bndrs. body)
-tryEtaReduce bndrs body eval_sd
- = go (reverse bndrs) body (mkRepReflCo (exprType body))
- where
- incoming_arity = count isId bndrs -- See Note [Eta reduction makes sense], point (2)
-
- go :: [Var] -- Binders, innermost first, types [a3,a2,a1]
- -> CoreExpr -- Of type tr
- -> Coercion -- Of type tr ~ ts
- -> Maybe CoreExpr -- Of type a1 -> a2 -> a3 -> ts
- -- See Note [Eta reduction with casted arguments]
- -- for why we have an accumulating coercion
- --
- -- Invariant: (go bs body co) returns an expression
- -- equivalent to (\(reverse bs). body |> co)
-
- -- See Note [Eta reduction with casted function]
- go bs (Cast e co1) co2
- = go bs e (co1 `mkTransCo` co2)
-
- go bs (Tick t e) co
- | tickishFloatable t
- = fmap (Tick t) $ go bs e co
- -- Float app ticks: \x -> Tick t (e x) ==> Tick t e
-
- go (b : bs) (App fun arg) co
- | Just (co', ticks) <- ok_arg b arg co (exprType fun)
- = fmap (flip (foldr mkTick) ticks) $ go bs fun co'
- -- Float arg ticks: \x -> e (Tick t x) ==> Tick t e
-
- go remaining_bndrs fun co
- | all isTyVar remaining_bndrs -- See Note [Eta reduction makes sense], point (1)
- , remaining_bndrs `ltLength` bndrs
- , ok_fun fun
- , let used_vars = exprFreeVars fun `unionVarSet` tyCoVarsOfCo co
- reduced_bndrs = mkVarSet (dropList remaining_bndrs bndrs)
- , used_vars `disjointVarSet` reduced_bndrs
- -- Check for any of the binders free in the result including the
- -- accumulated coercion
- -- See Note [Eta reduction makes sense], intro and point (1)
- = Just $ mkLams (reverse remaining_bndrs) (mkCast fun co)
-
- go _remaining_bndrs _fun _ = -- pprTrace "tER fail" (ppr _fun $$ ppr _remaining_bndrs) $
- Nothing
-
-
- ---------------
- -- See Note [Eta reduction makes sense], point (1)
- ok_fun (App fun (Type {})) = ok_fun fun
- ok_fun (Cast fun _) = ok_fun fun
- ok_fun (Tick _ expr) = ok_fun expr
- ok_fun (Var fun_id) = is_eta_reduction_sound fun_id || all ok_lam bndrs
- ok_fun _fun = False
-
- ---------------
- -- See Note [Eta reduction soundness], this is THE place to check soundness!
- is_eta_reduction_sound fun =
- -- Check that eta-reduction won't make the program stricter...
- (fun_arity fun >= incoming_arity -- criterion (A) and (E)
- || all_calls_with_arity incoming_arity) -- criterion (S)
- -- ... and that the function can be eta reduced to arity 0
- -- without violating invariants of Core and GHC
- && canEtaReduceToArity fun 0 0 -- criteria (L), (J), (W), (B)
- all_calls_with_arity n = isStrict (peelManyCalls n eval_sd)
- -- See Note [Eta reduction based on evaluation context]
-
- ---------------
- fun_arity fun -- See Note [Eta reduction makes sense], point (3)
- | isLocalId fun
- , isStrongLoopBreaker (idOccInfo fun) = 0
- | arity > 0 = arity
- | isEvaldUnfolding (idUnfolding fun) = 1
- -- See Note [Eta reduction soundness], criterion (E)
- | otherwise = 0
- where
- arity = idArity fun
-
- ---------------
- ok_lam v = isTyVar v || isEvVar v
-
- ---------------
- ok_arg :: Var -- Of type bndr_t
- -> CoreExpr -- Of type arg_t
- -> Coercion -- Of kind (t1~t2)
- -> Type -- Type (arg_t -> t1) of the function
- -- to which the argument is supplied
- -> Maybe (Coercion -- Of type (arg_t -> t1 ~ bndr_t -> t2)
- -- (and similarly for tyvars, coercion args)
- , [CoreTickish])
- -- See Note [Eta reduction with casted arguments]
- ok_arg bndr (Type ty) co _
- | Just tv <- getTyVar_maybe ty
- , bndr == tv = Just (mkHomoForAllCos [tv] co, [])
- ok_arg bndr (Var v) co fun_ty
- | bndr == v
- , let mult = idMult bndr
- , Just (fun_mult, _, _) <- splitFunTy_maybe fun_ty
- , mult `eqType` fun_mult -- There is no change in multiplicity, otherwise we must abort
- = Just (mkFunResCo Representational (idScaledType bndr) co, [])
- ok_arg bndr (Cast e co_arg) co fun_ty
- | (ticks, Var v) <- stripTicksTop tickishFloatable e
- , Just (fun_mult, _, _) <- splitFunTy_maybe fun_ty
- , bndr == v
- , fun_mult `eqType` idMult bndr
- = Just (mkFunCo Representational (multToCo fun_mult) (mkSymCo co_arg) co, ticks)
- -- The simplifier combines multiple casts into one,
- -- so we can have a simple-minded pattern match here
- ok_arg bndr (Tick t arg) co fun_ty
- | tickishFloatable t, Just (co', ticks) <- ok_arg bndr arg co fun_ty
- = Just (co', t:ticks)
-
- ok_arg _ _ _ _ = Nothing
-
--- | Can we eta-reduce the given function to the specified arity?
--- See Note [Eta reduction soundness], criteria (B), (J), (W) and (L).
-canEtaReduceToArity :: Id -> JoinArity -> Arity -> Bool
-canEtaReduceToArity fun dest_join_arity dest_arity =
- not $
- hasNoBinding fun -- (B)
- -- Don't undersaturate functions with no binding.
-
- || ( isJoinId fun && dest_join_arity < idJoinArity fun ) -- (J)
- -- Don't undersaturate join points.
- -- See Note [Invariants on join points] in GHC.Core, and #20599
-
- || ( dest_arity < idCbvMarkArity fun ) -- (W)
- -- Don't undersaturate StrictWorkerIds.
- -- See Note [Strict Worker Ids] in GHC.CoreToStg.Prep.
-
- || isLinearType (idType fun) -- (L)
- -- Don't perform eta reduction on linear types.
- -- If `f :: A %1-> B` and `g :: A -> B`,
- -- then `g x = f x` is OK but `g = f` is not.
{- *********************************************************************
* *