Moduleghc-9.10.3GHC2021
GHC.Types.Demand
A language to express the evaluation context of an expression as a Demand and track how an expression evaluates free variables and arguments in turn as a DmdType.
Lays out the abstract domain for GHC.Core.Opt.DmdAnal.
- 12 types
- 101 values
- Packageghc-9.10.3
- Exports120
- LanguageGHC2021
- LicenceBSD-3-Clause
- SourceDemand.hs
Demands
15 declarationsDescribes an interval of evaluation cardinalities. See Note [Evaluation cardinalities] See Note [Bit vector representation for Card]
Instances4Eq, Show, Outputable, Binary
Absent, {0}. Pretty-printed as A.
Used at most once, {0,1}. Pretty-printed as M.
Every possible cardinality; the top element, {0,1,n}. Pretty-printed as L.
Bottom, {}. Pretty-printed as A.
Strict and used once, {1}. Pretty-printed as 1.
Strict and used (possibly) many times, {1,n}. Pretty-printed as S.
A demand describes
How many times a variable is evaluated, via a Cardinality, and
How deep its value was evaluated in turn, via a SubDemand.
Examples (using Note [Demand notation]):
seq puts demand `1A` on its first argument: It evaluates the argument strictly (`1`), but not any deeper (
A).fst puts demand `1P(1L,A)` on its argument: It evaluates the argument pair strictly and the first component strictly, but no nested info beyond that (
L). Its second argument is not used at all.$ puts demand `1C(1,L)` on its first argument: It calls (
C) the argument function with one argument, exactly once (`1`). No info on how the result of that call is evaluated (L).maybe puts demand `MC(M,L)` on its second argument: It evaluates the argument function at most once ((M)aybe) and calls it once when it is evaluated.
`fst p + fst p` puts demand `SP(SL,A)` on
p: It's `1P(1L,A)` multiplied by two, so we getS(used at least once, possibly multiple times).
This data type is quite similar to `Scaled SubDemand', but it's scaled
by Card, which is an interval on Multiplicity, the upper bound of
which could be used to infer uniqueness types. Also we treat AbsDmd and
BotDmd specially, as the concept of a SubDemand doesn't apply when there
isn't any evaluation at all. If you don't care, simply use (:*).
c :* sd is a demand that says "evaluated c times, and any trace in
which it is evaluated will evaluate at least as deep as sd".
Matching on this pattern synonym is a complete match.
If the matched demand was AbsDmd, it will match as C_00 :* seqSubDmd.
If the matched demand was BotDmd, it will match as C_10 :* botSubDmd.
The builder of this pattern synonym simply discards the SubDemand if the
Card was absent and returns AbsDmd or BotDmd instead. It will assert
that the discarded sub-demand was seqSubDmd and botSubDmd, respectively.
Call sites should consider whether they really want to look at the SubDemand of an absent demand and match on AbsDmd and/or BotDmd otherwise. Really, any other SubDemand would be allowed and might work better, depending on context.
A sub-demand describes an evaluation context (in the sense of an operational semantics), e.g. how deep the denoted thing is going to be evaluated. See Demand for examples.
See Note [SubDemand denotes at least one evaluation] for a more detailed description of what a sub-demand means.
See Note [Demand notation] for the extensively used short-hand notation. See also Note [Why Boxity in SubDemand and not in Demand?].
Constructors
Poly !Boxity !CardNonOncePolymorphic demand, the denoted thing is evaluated arbitrarily deep, with the specified cardinality at every level. The Boxity applies only to the outer evaluation context as well as all inner evaluation context. See Note [Boxity in Poly] for why we want it to carry Boxity. Expands to
CallviaviewCalland to Prod via viewProd.Poly b nis semantically equivalent toProd b [n :* Poly b n, ...] orCall n (Poly Boxed n)@.viewCalland viewProd do these rewrites.In Note [Demand notation]:
L === P(L,L,...)andL === C(L),B === P(B,B,...)andB === C(B),!A === !P(A,A,...)and!A === C(A), and so on.We'll only see Poly with C_10 (B), C_00 (A), C_0N (L) and sometimes C_1N (S) through plusSubDmd, never C_01 (M) or C_11 (1) (grep the source code). Hence CardNonOnce, which is closed under
lubandplus.Why doesn't this constructor simply carry a Demand instead of its fields? See Note [Call SubDemand vs. evaluation Demand].
Prod !Boxity ![Demand]Prod b dsdescribes the evaluation context of a case scrutinisation on an expression of product type, where the product components are evaluated according tods. The Boxitybsays whether or not the box of the product was used.
A smart constructor for Prod, applying rewrite rules along the semantic
equality Prod b [n :* Poly Boxed n, ...] === Poly b n, simplifying to
Poly SubDemands when possible. Examples:
Rewrites
P(L,L)(e.g., argumentsBoxed,[L,L]) toLRewrites
!P(L!L,L!L)(e.g., argumentsUnboxed,[L!L,L!L]) to!LDoes not rewrite
P(1L),P(L!L),!P(L)orP(L,A)
Algebra
Least upper bound
Denotes ∪ on Card.
Denotes ∪ on Demand.
Greatest lower bound
Denotes ∩ on Card.
Plus
Multiply
Predicates on Cardinalities and Demands
True = upper bound is 0.
True = upper bound is 1.
True = lower bound is 1.
Is the value used at most once?
Not absent and used strictly. See Note [Strict demands]
Contrast with isStrictUsedDmd. See Note [Strict demands]
Used to suppress pretty-printing of an uninformative demand
We try to avoid tracking weak free variable demands in strictness signatures for analysis performance reasons. See Note [Lazy and unleashable free variables] in GHC.Core.Opt.DmdAnal.
True when the signature indicates all arguments are boxed
Special demands
Demands used in PrimOp signatures
First argument of catch#: MC(1,L).
Evaluates its arg lazily, but then applies it exactly once to one argument.
Second argument of catch#: MC(1,C(1,L)).
Evaluates its arg lazily, but then applies it exactly once to two arguments.
First argument of maskAsyncExceptions#: 1C(1,L).
Called exactly once.
First argument of atomically#: SC(S,L).
Called at least once, possibly many times.
Other Demand operations
Intersect with [0,1].
Make a Demand evaluated at-most-once.
Make a Demand evaluated at-least-once (e.g. strict).
If the argument is a guaranteed-terminating type (i.e. a non-newtype dictionary) give it strict demand. This is sound because terminating types can't be bottom: See GHC.Core Note [NON-BOTTOM-DICTS invariant] Also split the product type & demand and recur in order to similarly strictify the argument's contained used non-newtype superclass dictionaries. We use the demand as our recursive measure to guarantee termination.
Make a Demand lazy.
Adjust the demand on a binding that may float outwards See Note [Floatifying demand info when floating]
Peels one call level from the sub-demand, and also returns how many times we entered the lambda body.
Wraps the SubDemand with a one-shot call demand: d -> C(1,d).
mkCalledOnceDmds n d returns C(1,C1...C(1,d)) where there are n C1's.
Extracting one-shot information
See Note [Computing one-shot info]
See Note [Computing one-shot info]
See Note [Computing one-shot info]
saturatedByOneShots n C(M,C(M,...)) = True
=
There are at least n nested C(M,..) calls.
See Note [Demand on the worker] in GHC.Core.Opt.WorkWrap
Manipulating Boxity of a Demand
Divergence
6 declarationsDivergence characterises whether something surely diverges. Models a subset lattice of the following exhaustive set of divergence results:
- n
nontermination (e.g. loops)
- i
throws imprecise exception
- p
throws precise exception
- c
converges (reduces to WHNF).
The different lattice elements correspond to different subsets, indicated by juxtaposition of indicators (e.g. nc definitely doesn't throw an exception, and may or may not reduce to WHNF).
Dunno (nipc)
|
ExnOrDiv (nip)
|
Diverges (ni)
As you can see, we don't distinguish n and i. See Note [Precise exceptions and strictness analysis] for why p is so special compared to i.
Constructors
DivergesDefinitely throws an imprecise exception or diverges.
ExnOrDivDefinitely throws a *precise* exception, an imprecise exception or diverges. Never converges, hence isDeadEndDiv! See scenario 1 in Note [Precise exceptions and strictness analysis].
DunnoMight diverge, throw any kind of exception or converge.
Instances3Eq, Outputable, Binary
Eq DivergenceDefined in ghc-9.10.3 · GHC.Types.DemandOutputable DivergenceDefined in ghc-9.10.3 · GHC.Types.DemandBinary DivergenceDefined in ghc-9.10.3 · GHC.Types.Demand
True if the Divergence indicates that evaluation will not return. See Note [Dead ends].
Demand environments
7 declarationsBuild a potentially terminating DmdEnv from a finite map that says what has been evaluated so far
DmdEnv is a monoid via plusDmdEnv and nopDmdEnv; this is its msum
Demand types
2 declarationsCharacterises how an expression
Algebra
The demand type of doing nothing (lazy, absent, no Divergence
information). Note that it is 'not' the top of the lattice (which would be
"may use everything"), so it is (no longer) called topDmdType.
Compute the least upper bound of two DmdTypes elicited /by the same incoming demand/!
Other operations
When e is evaluated after executing an IO action that may throw a precise exception, we act as if there is an additional control flow path that is taken if e throws a precise exception. The demand type of this control flow path * is lazy and absent (topDmd) and boxed in all free variables and arguments * has exnDiv Divergence result See Note [Precise exceptions and strictness analysis]
So we can simply take a variant of nopDmdType, exnDmdType.
Why not nopDmdType? Because then the result of e can never be exnDiv!
That means failure to drop dead-ends, see #18086.
Demand signatures
15 declarationsThe depth of the wrapped DmdType encodes the arity at which it is safe to unleash. Better construct this through mkDmdSigForArity. See Note [Understanding DmdType and DmdSig]
True if the signature diverges or throws an imprecise exception in a saturated call. NB: In constrast to isDeadEndSig this returns False for exnDiv. See Note [Dead ends] and Note [Precise vs imprecise exceptions].
True if the signature diverges or throws an exception in a saturated call. See Note [Dead ends].
Returns true if an application to n value args would diverge or throw an exception.
If a function having botDiv is applied to a less number of arguments than its syntactic arity, we cannot say for sure that it is going to diverge. Hence this function conservatively returns False in that case. See Note [Dead ends].
Handling arity adjustments
Add extra (topDmd) arguments to a strictness signature. In contrast to etaConvertDmdSig, this prepends additional argument demands. This is used by FloatOut.
We are expanding (x y. e) to (x y z. e z) or reducing from the latter to the former (when the Simplifier identifies a new join points, for example). In contrast to prependArgsDmdSig, this appends extra arg demands if necessary. This works by looking at the DmdType (which was produced under a call demand for the old arity) and trying to transfer as many facts as we can to the call demand of new arity. An arity increase (resulting in a stronger incoming demand) can retain much of the info, while an arity decrease (a weakening of the incoming demand) must fall back to a conservative default.
Demand transformers from demand signatures
4 declarationsA demand transformer is a monotone function from an incoming evaluation context (SubDemand) to a DmdType, describing how the denoted thing (i.e. expression, function) uses its arguments and free variables, and whether it diverges.
See Note [Understanding DmdType and DmdSig] and Note [What are demand signatures?].
Extrapolate a demand signature (DmdSig) into a DmdTransformer.
Given a function's DmdSig and a SubDemand for the evaluation context, return how the function evaluates its free variables and arguments.
A special DmdTransformer for data constructors that feeds product demands into the constructor arguments.
A special DmdTransformer for dictionary selectors that feeds the demand on the result into the indicated dictionary component (if saturated). See Note [Demand transformer for a dictionary selector].
Trim to a type shape
3 declarationsInstances1Outputable
Outputable TypeShapeDefined in ghc-9.10.3 · GHC.Types.Demand
Drop all boxity
seqing stuff
4 declarationsZapping usage information
4 declarationsRemove the demand environment from the signature.
Remove all `C_01 :*` info (but not CM sub-demands) from the demand
Remove all `C_01 :*` info (but not CM sub-demands) from the strictness
signature