diff options
author | Sebastian Graf <sebastian.graf@kit.edu> | 2021-02-22 18:37:41 +0100 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2021-03-03 08:12:40 -0500 |
commit | 3630b9baa3887a5bf5cb3ba12e48301fe6cce173 (patch) | |
tree | 9a0217f977baaa84295c6cda36064d5085d2d6c3 /compiler/GHC/Core | |
parent | 5c4dcc3e3735544bcc7c1bccbe7fd9e5db908c23 (diff) | |
download | haskell-3630b9baa3887a5bf5cb3ba12e48301fe6cce173.tar.gz |
DmdAnal: Better syntax for demand signatures (#19016)
The update of the Outputable instance resulted in a slew of
documentation changes within Notes that used the old syntax.
The most important doc changes are to `Note [Demand notation]`
and the user's guide.
Fixes #19016.
Diffstat (limited to 'compiler/GHC/Core')
-rw-r--r-- | compiler/GHC/Core/Opt/DmdAnal.hs | 61 |
1 files changed, 20 insertions, 41 deletions
diff --git a/compiler/GHC/Core/Opt/DmdAnal.hs b/compiler/GHC/Core/Opt/DmdAnal.hs index 413da0794a..eedc9b4489 100644 --- a/compiler/GHC/Core/Opt/DmdAnal.hs +++ b/compiler/GHC/Core/Opt/DmdAnal.hs @@ -145,7 +145,7 @@ Consider a CoreProgram like where e* are exported, but n* are not. Intuitively, we can see that @n1@ is only ever called with two arguments and in every call site, the first component of the result of the call -is evaluated. Thus, we'd like it to have idDemandInfo @UCU(C1(P(SU,A))@. +is evaluated. Thus, we'd like it to have idDemandInfo @LCL(CM(P(1L,A))@. NB: We may *not* give e2 a similar annotation, because it is exported and external callers might use it in arbitrary ways, expressed by 'topDmd'. This can then be exploited by Nested CPR and eta-expansion, @@ -313,24 +313,6 @@ dmdAnalBindLetDown top_lvl env dmd bind anal_body = case bind of -- the vanilla call demand seem to be due to (b). So we don't -- bother to re-analyse the RHS. -{- -Note [Ensure demand is strict] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It's important not to analyse e with a lazy demand because -a) When we encounter case s of (a,b) -> - we demand s with U(d1d2)... but if the overall demand is lazy - that is wrong, and we'd need to reduce the demand on s, - which is inconvenient -b) More important, consider - f (let x = R in x+x), where f is lazy - We still want to mark x as demanded, because it will be when we - enter the let. If we analyse f's arg with a Lazy demand, we'll - just mark x as Lazy -c) The application rule wouldn't be right either - Evaluating (f x) in a L demand does *not* cause - evaluation of f in a C(L) demand! --} - -- If e is complicated enough to become a thunk, its contents will be evaluated -- at most once, so oneify it. dmdTransformThunkDmd :: CoreExpr -> Demand -> Demand @@ -357,9 +339,6 @@ dmdAnal, dmdAnal' :: AnalEnv -> SubDemand -- The main one takes a *SubDemand* -> CoreExpr -> (DmdType, CoreExpr) --- The SubDemand is always strict and not absent --- See Note [Ensure demand is strict] - dmdAnal env d e = -- pprTrace "dmdAnal" (ppr d <+> ppr e) $ dmdAnal' env d e @@ -649,7 +628,7 @@ But note that these include the demand on the case binder; see Note [Demand on case-alternative binders] in GHC.Types.Demand. This is crucial. Example: f x = case x of y { (a,b) -> k y a } -If we just take scrut_demand = U(L,A), then we won't pass x to the +If we just take scrut_demand = 1P(L,A), then we won't pass x to the worker, so the worker will rebuild x = (a, absent-error) and that'll crash. @@ -761,12 +740,12 @@ dmdTransform env var dmd -- | @dmdAnalRhsSig@ analyses the given RHS to compute a demand signature -- for the LetDown rule. It works as follows: -- --- * assuming a demand of <U> +-- * assuming the weakest possible body sub-demand, L -- * looking at the definition -- * determining a strictness signature -- --- Since it assumed a demand of <U>, the resulting signature is applicable at --- any call site. +-- Since it assumed a body sub-demand of L, the resulting signature is +-- applicable at any call site. dmdAnalRhsSig :: TopLevelFlag -> RecFlag @@ -906,10 +885,10 @@ look a little puzzling. E.g. ( B -> j 4 ) ( C -> \y. blah ) -The entire thing is in a C(S) context, so j's strictness signature +The entire thing is in a C1(L) context, so j's strictness signature will be [A]b meaning one absent argument, returns bottom. That seems odd because -there's a \y inside. But it's right because when consumed in a C(1) +there's a \y inside. But it's right because when consumed in a C1(L) context the RHS of the join point is indeed bottom. Note [Demand signatures are computed for a threshold demand based on idArity] @@ -940,12 +919,12 @@ arguments than idArity. Example: then \y -> ... y ... else \y -> ... y ... -We'd analyse `f` under a unary call demand C(S), corresponding to idArity +We'd analyse `f` under a unary call demand C1(L), corresponding to idArity being 1. That's enough to look under the manifest lambda and find out how a unary call would use `x`, but not enough to look into the lambdas in the if branches. -On the other hand, if we analysed for call demand C(C(S)), we'd get useful +On the other hand, if we analysed for call demand C1(C1(L)), we'd get useful strictness info for `y` (and more precise info on `x`) and possibly CPR information, but @@ -977,7 +956,7 @@ Consider the following expression, for example: (let go x y = `x` seq ... in go) |> co -`go` might have a strictness signature of `<S><L>`. The simplifier will identify +`go` might have a strictness signature of `<1L><L>`. The simplifier will identify `go` as a nullary join point through `joinPointBinding_maybe` and float the coercion into the binding, leading to an arity decrease: @@ -1058,11 +1037,11 @@ Here we *really* want to unbox z, even though it appears to be used boxed in the Nil case. Partly the Nil case is not a hot path. But more specifically, the whole function gets the CPR property if we do. -That motivated using a demand of C(C(C(S(L,L)))) for the RHS, where +That motivated using a demand of C1(C1(C1(P(L,L)))) for the RHS, where (solely because the result was a product) we used a product demand (albeit with lazy components) for the body. But that gives very silly behaviour -- see #17932. Happily it turns out now to be entirely -unnecessary: we get good results with C(C(C(S))). So I simply +unnecessary: we get good results with C1(C1(C1(L))). So I simply deleted the special case. -} @@ -1169,7 +1148,7 @@ Now to the reasons. For (1) consider A g1 -> case (x |> g1) of (p,q) -> ... B -> error "urk" -where A,B are the constructors of a GADT. We'll get a U(U,U) demand +where A,B are the constructors of a GADT. We'll get a 1P(L,L) demand on x from the A branch, but that's a stupid demand for x itself, which has type 'a'. Indeed we get ASSERTs going off (notably in splitUseProdDmd, #8569). @@ -1180,7 +1159,7 @@ For (2) consider f 0 _ = 0 f _ (MkT n t) = f n t -Here f is lazy in T, but its *usage* is infinite: U(U,U(U,U(U, ...))). +Here f is lazy in T, but its *usage* is infinite: P(L,P(L,P(L, ...))). Notice that this happens because T is a product type, and is recrusive. If we are not careful, we'll fail to iterate to a fixpoint in dmdFix, and bale out entirely, which is inefficient and over-conservative. @@ -1221,11 +1200,11 @@ addVarDmd (DmdType fv ds res) var dmd addLazyFVs :: DmdType -> DmdEnv -> DmdType addLazyFVs dmd_ty lazy_fvs = dmd_ty `plusDmdType` mkPlusDmdArg lazy_fvs - -- Using bothDmdType (rather than just both'ing the envs) + -- Using plusDmdType (rather than just plus'ing the envs) -- is vital. Consider -- let f = \x -> (x,y) -- in error (f 3) - -- Here, y is treated as a lazy-fv of f, but we must `bothDmd` that L + -- Here, y is treated as a lazy-fv of f, but we must `plusDmd` that L -- demand with the bottom coming up from 'error' -- -- I got a loop in the fixpointer without this, due to an interaction @@ -1521,10 +1500,10 @@ Note [Final Demand Analyser run] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some of the information that the demand analyser determines is not always preserved by the simplifier. For example, the simplifier will happily rewrite - \y [Demand=1*U] let x = y in x + x + \y [Demand=MU] let x = y in x + x to - \y [Demand=1*U] y + y -which is quite a lie. + \y [Demand=MU] y + y +which is quite a lie: Now y occurs more than just once. The once-used information is (currently) only used by the code generator, though. So: @@ -1541,7 +1520,7 @@ generator, though. So: This way, correct information finds its way into the module interface (strictness signatures!) and the code generator (single-entry thunks!) -Note that, in contrast, the single-call information (C1(..)) /can/ be +Note that, in contrast, the single-call information (CM(..)) /can/ be relied upon, as the simplifier tends to be very careful about not duplicating actual function calls. |