Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
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.
Synopsis
- data Card
- data Demand = !Card :* !SubDemand
- data SubDemand = Prod ![Demand]
- mkProd :: [Demand] -> SubDemand
- viewProd :: Arity -> SubDemand -> Maybe [Demand]
- absDmd :: Demand
- topDmd :: Demand
- botDmd :: Demand
- seqDmd :: Demand
- topSubDmd :: SubDemand
- lubCard :: Card -> Card -> Card
- lubDmd :: Demand -> Demand -> Demand
- lubSubDmd :: SubDemand -> SubDemand -> SubDemand
- plusCard :: Card -> Card -> Card
- plusDmd :: Demand -> Demand -> Demand
- plusSubDmd :: SubDemand -> SubDemand -> SubDemand
- multCard :: Card -> Card -> Card
- multDmd :: Card -> Demand -> Demand
- multSubDmd :: Card -> SubDemand -> SubDemand
- isAbs :: Card -> Bool
- isUsedOnce :: Card -> Bool
- isStrict :: Card -> Bool
- isAbsDmd :: Demand -> Bool
- isUsedOnceDmd :: Demand -> Bool
- isStrUsedDmd :: Demand -> Bool
- isStrictDmd :: Demand -> Bool
- isTopDmd :: Demand -> Bool
- isSeqDmd :: Demand -> Bool
- isWeakDmd :: Demand -> Bool
- evalDmd :: Demand
- lazyApply1Dmd :: Demand
- lazyApply2Dmd :: Demand
- strictOnceApply1Dmd :: Demand
- strictManyApply1Dmd :: Demand
- oneifyCard :: Card -> Card
- oneifyDmd :: Demand -> Demand
- strictifyDmd :: Demand -> Demand
- strictifyDictDmd :: Type -> Demand -> Demand
- mkWorkerDemand :: Int -> Demand
- peelCallDmd :: SubDemand -> (Card, SubDemand)
- peelManyCalls :: Int -> SubDemand -> Card
- mkCalledOnceDmd :: SubDemand -> SubDemand
- mkCalledOnceDmds :: Arity -> SubDemand -> SubDemand
- addCaseBndrDmd :: SubDemand -> [Demand] -> [Demand]
- argOneShots :: Demand -> [OneShotInfo]
- argsOneShots :: StrictSig -> Arity -> [[OneShotInfo]]
- saturatedByOneShots :: Int -> Demand -> Bool
- type DmdEnv = VarEnv Demand
- emptyDmdEnv :: VarEnv Demand
- keepAliveDmdEnv :: DmdEnv -> IdSet -> DmdEnv
- reuseEnv :: DmdEnv -> DmdEnv
- data Divergence
- topDiv :: Divergence
- botDiv :: Divergence
- exnDiv :: Divergence
- lubDivergence :: Divergence -> Divergence -> Divergence
- isDeadEndDiv :: Divergence -> Bool
- data DmdType = DmdType {}
- dmdTypeDepth :: DmdType -> Arity
- nopDmdType :: DmdType
- botDmdType :: DmdType
- lubDmdType :: DmdType -> DmdType -> DmdType
- plusDmdType :: DmdType -> PlusDmdArg -> DmdType
- multDmdType :: Card -> DmdType -> DmdType
- type PlusDmdArg = (DmdEnv, Divergence)
- mkPlusDmdArg :: DmdEnv -> PlusDmdArg
- toPlusDmdArg :: DmdType -> PlusDmdArg
- peelFV :: DmdType -> Var -> (DmdType, Demand)
- findIdDemand :: DmdType -> Var -> Demand
- addDemand :: Demand -> DmdType -> DmdType
- splitDmdTy :: DmdType -> (Demand, DmdType)
- deferAfterPreciseException :: DmdType -> DmdType
- keepAliveDmdType :: DmdType -> VarSet -> DmdType
- newtype StrictSig = StrictSig DmdType
- mkStrictSigForArity :: Arity -> DmdType -> StrictSig
- mkClosedStrictSig :: [Demand] -> Divergence -> StrictSig
- splitStrictSig :: StrictSig -> ([Demand], Divergence)
- strictSigDmdEnv :: StrictSig -> DmdEnv
- hasDemandEnvSig :: StrictSig -> Bool
- nopSig :: StrictSig
- botSig :: StrictSig
- isTopSig :: StrictSig -> Bool
- isDeadEndSig :: StrictSig -> Bool
- appIsDeadEnd :: StrictSig -> Int -> Bool
- prependArgsStrictSig :: Int -> StrictSig -> StrictSig
- etaConvertStrictSig :: Arity -> StrictSig -> StrictSig
- type DmdTransformer = SubDemand -> DmdType
- dmdTransformSig :: StrictSig -> DmdTransformer
- dmdTransformDataConSig :: Arity -> DmdTransformer
- dmdTransformDictSelSig :: StrictSig -> DmdTransformer
- data TypeShape
- trimToType :: Demand -> TypeShape -> Demand
- seqDemand :: Demand -> ()
- seqDemandList :: [Demand] -> ()
- seqDmdType :: DmdType -> ()
- seqStrictSig :: StrictSig -> ()
- zapUsageDemand :: Demand -> Demand
- zapDmdEnvSig :: StrictSig -> StrictSig
- zapUsedOnceDemand :: Demand -> Demand
- zapUsedOnceSig :: StrictSig -> StrictSig
Demands
Describes an interval of evaluation cardinalities. See Note [Evaluation cardinalities]
C_00 | {0} Absent. |
C_01 | {0,1} Used at most once. |
C_0N | {0,1,n} Every possible cardinality; the top element. |
C_11 | {1} Strict and used once. |
C_1N | {1,n} Strict and used (possibly) many times. |
C_10 | {} The empty interval; the bottom element of the lattice. |
A demand describes a scaled evaluation context, e.g. how many times and how deep the denoted thing is evaluated.
The "how many" component is represented by a Card
inality.
The "how deep" component is represented by a SubDemand
.
Examples (using Note [Demand notation]):
seq
puts demand1A
on its first argument: It evaluates the argument strictly (1
), but not any deeper (A
).fst
puts demand1P(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 demand1C1(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 demandMCM(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 demandSP(SL,A)
onp
: It's1P(1L,A)
multiplied by two, so we getS
(used at least once, possibly multiple times).
This data type is quite similar to
, but it's scaled
by Scaled
SubDemand
Card
, which is an interval on Multiplicity
, the upper bound of
which could be used to infer uniqueness types.
A sub-demand describes an evaluation context, e.g. how deep the
denoted thing is evaluated. See Demand
for examples.
The nested SubDemand
d
of a Call
Cn(d)
is relative to a single such call.
E.g. The expression f 1 2 + f 3 4
puts call demand SCS(C1(L))
on f
:
f
is called exactly twice (S
), each time exactly once (1
) with an
additional argument.
The nested Demand
s dn
of a Prod
P(d1,d2,...)
apply absolutely:
If dn
is a used once demand (cf. isUsedOnce
), then that means that
the denoted sub-expression is used once in the entire evaluation context
described by the surrounding Demand
. E.g., LP(ML)
means that the
field of the denoted expression is used at most once, although the
entire expression might be used many times.
See Note [Call demands are relative] and Note [Demand notation].
Prod ![Demand] |
|
viewProd :: Arity -> SubDemand -> Maybe [Demand] Source #
viewProd n sd
interprets sd
as a Prod
of arity n
, expanding Poly
demands as necessary.
Algebra
Least upper bound
Plus
Multiply
Predicates on Card
inalities and Demand
s
isUsedOnceDmd :: Demand -> Bool Source #
Is the value used at most once?
isStrUsedDmd :: Demand -> Bool Source #
Not absent and used strictly. See Note [Strict demands]
isStrictDmd :: Demand -> Bool Source #
Contrast with isStrictUsedDmd. See Note [Strict demands]
isWeakDmd :: Demand -> Bool Source #
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.
Special demands
Demands used in PrimOp signatures
lazyApply1Dmd :: Demand Source #
First argument of catch#: MCM(L)
.
Evaluates its arg lazily, but then applies it exactly once to one argument.
lazyApply2Dmd :: Demand Source #
Second argument of catch#: MCM(C1(L))
.
Calls its arg lazily, but then applies it exactly once to an additional argument.
strictOnceApply1Dmd :: Demand Source #
First argument of 'GHC.Exts.maskAsyncExceptions#': 1C1(L)
.
Called exactly once.
strictManyApply1Dmd :: Demand Source #
First argument of 'GHC.Exts.atomically#': SCS(L)
.
Called at least once, possibly many times.
Other Demand
operations
oneifyCard :: Card -> Card Source #
Intersect with [0,1].
strictifyDictDmd :: Type -> Demand -> Demand Source #
If the argument is a used non-newtype dictionary, give it strict demand. 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.
mkWorkerDemand :: Int -> Demand Source #
peelCallDmd :: SubDemand -> (Card, SubDemand) Source #
Peels one call level from the sub-demand, and also returns how many times we entered the lambda body.
mkCalledOnceDmd :: SubDemand -> SubDemand Source #
Wraps the SubDemand
with a one-shot call demand: d
-> C1(d)
.
mkCalledOnceDmds :: Arity -> SubDemand -> SubDemand Source #
mkCalledOnceDmds n d
returns C1(C1...(C1 d))
where there are n
C1
's.
Extracting one-shot information
:: Demand | depending on saturation |
-> [OneShotInfo] |
See Note [Computing one-shot info]
argsOneShots :: StrictSig -> Arity -> [[OneShotInfo]] Source #
See Note [Computing one-shot info]
saturatedByOneShots :: Int -> Demand -> Bool Source #
saturatedByOneShots n CM(CM(...)) = True
=
There are at least n nested CM(..) calls.
See Note [Demand on the worker] in GHC.Core.Opt.WorkWrap
Demand environments
keepAliveDmdEnv :: DmdEnv -> IdSet -> DmdEnv Source #
keepAliveDmdType dt vs
makes sure that the Ids in vs
have
some usage in the returned demand types -- they are not Absent.
See Note [Absence analysis for stable unfoldings and RULES]
in GHC.Core.Opt.DmdAnal.
Divergence
data Divergence Source #
Divergence
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 exceTtion
- 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.
Diverges | Definitely throws an imprecise exception or diverges. |
ExnOrDiv | Definitely throws a *precise* exception, an imprecise
exception or diverges. Never converges, hence |
Dunno | Might diverge, throw any kind of exception or converge. |
Instances
Binary Divergence # | |
Defined in GHC.Types.Demand put_ :: BinHandle -> Divergence -> IO () Source # put :: BinHandle -> Divergence -> IO (Bin Divergence) Source # | |
Outputable Divergence # | |
Defined in GHC.Types.Demand ppr :: Divergence -> SDoc Source # | |
Eq Divergence # | |
Defined in GHC.Types.Demand (==) :: Divergence -> Divergence -> Bool # (/=) :: Divergence -> Divergence -> Bool # |
topDiv :: Divergence Source #
botDiv :: Divergence Source #
exnDiv :: Divergence Source #
lubDivergence :: Divergence -> Divergence -> Divergence Source #
isDeadEndDiv :: Divergence -> Bool Source #
True if the Divergence
indicates that evaluation will not return.
See Note [Dead ends].
Demand types
Characterises how an expression
* Evaluates its free variables (dt_env
)
* Evaluates its arguments (dt_args
)
* Diverges on every code path or not (dt_div
)
dmdTypeDepth :: DmdType -> Arity Source #
Algebra
nopDmdType :: DmdType Source #
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.
botDmdType :: DmdType Source #
lubDmdType :: DmdType -> DmdType -> DmdType Source #
Compute the least upper bound of two DmdType
s elicited /by the same
incoming demand/!
plusDmdType :: DmdType -> PlusDmdArg -> DmdType Source #
PlusDmdArg
type PlusDmdArg = (DmdEnv, Divergence) Source #
mkPlusDmdArg :: DmdEnv -> PlusDmdArg Source #
toPlusDmdArg :: DmdType -> PlusDmdArg Source #
Other operations
deferAfterPreciseException :: DmdType -> DmdType Source #
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
) in all free variables and arguments
* has exnDiv
Divergence
result
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.
See Note [Precise exceptions and strictness analysis]
keepAliveDmdType :: DmdType -> VarSet -> DmdType Source #
See keepAliveDmdEnv
.
Demand signatures
The depth of the wrapped DmdType
encodes the arity at which it is safe
to unleash. Better construct this through mkStrictSigForArity
.
See Note [Understanding DmdType and StrictSig]
mkClosedStrictSig :: [Demand] -> Divergence -> StrictSig Source #
splitStrictSig :: StrictSig -> ([Demand], Divergence) Source #
strictSigDmdEnv :: StrictSig -> DmdEnv Source #
hasDemandEnvSig :: StrictSig -> Bool Source #
isDeadEndSig :: StrictSig -> Bool Source #
True if the signature diverges or throws an exception in a saturated call. See Note [Dead ends].
appIsDeadEnd :: StrictSig -> Int -> Bool Source #
Returns true if an application to n 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
prependArgsStrictSig :: Int -> StrictSig -> StrictSig Source #
Add extra (topDmd
) arguments to a strictness signature.
In contrast to etaConvertStrictSig
, this prepends additional argument
demands. This is used by FloatOut.
etaConvertStrictSig :: Arity -> StrictSig -> StrictSig Source #
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 prependArgsStrictSig
, 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
type DmdTransformer = SubDemand -> DmdType Source #
A 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 StrictSig] and Note [What are demand signatures?].
dmdTransformSig :: StrictSig -> DmdTransformer Source #
Extrapolate a demand signature (StrictSig
) into a DmdTransformer
.
Given a function's StrictSig
and a SubDemand
for the evaluation context,
return how the function evaluates its free variables and arguments.
dmdTransformDataConSig :: Arity -> DmdTransformer Source #
A special DmdTransformer
for data constructors that feeds product
demands into the constructor arguments.
dmdTransformDictSelSig :: StrictSig -> DmdTransformer Source #
A special DmdTransformer
for dictionary selectors that feeds the demand
on the result into the indicated dictionary component (if saturated).
Trim to a type shape
Instances
seq
ing stuff
seqDemandList :: [Demand] -> () Source #
seqDmdType :: DmdType -> () Source #
seqStrictSig :: StrictSig -> () Source #
Zapping usage information
zapUsageDemand :: Demand -> Demand Source #
zapDmdEnvSig :: StrictSig -> StrictSig Source #
Remove the demand environment from the signature.
zapUsedOnceDemand :: Demand -> Demand Source #
Remove all `C_01 :*` info (but not CM
sub-demands) from the demand
zapUsedOnceSig :: StrictSig -> StrictSig Source #
Remove all `C_01 :*` info (but not CM
sub-demands) from the strictness
signature