Safe Haskell | None |
---|---|
Language | Haskell2010 |
GHC.Types.Demand
Description
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 Boxity
- data Card where
- type CardNonAbs = Card
- type CardNonOnce = Card
- data Demand where
- data SubDemand
- mkProd :: Boxity -> [Demand] -> SubDemand
- viewProd :: Arity -> SubDemand -> Maybe (Boxity, [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
- isWeakDmd :: Demand -> Bool
- onlyBoxedArguments :: DmdSig -> Bool
- evalDmd :: Demand
- lazyApply1Dmd :: Demand
- lazyApply2Dmd :: Demand
- strictOnceApply1Dmd :: Demand
- strictManyApply1Dmd :: Demand
- oneifyCard :: Card -> Card
- oneifyDmd :: Demand -> Demand
- strictifyDmd :: Demand -> Demand
- strictifyDictDmd :: Type -> Demand -> Demand
- lazifyDmd :: Demand -> Demand
- peelCallDmd :: SubDemand -> (Card, SubDemand)
- peelManyCalls :: Arity -> SubDemand -> (Card, SubDemand)
- mkCalledOnceDmd :: SubDemand -> SubDemand
- mkCalledOnceDmds :: Arity -> SubDemand -> SubDemand
- mkWorkerDemand :: Int -> Demand
- subDemandIfEvaluated :: Demand -> SubDemand
- argOneShots :: Demand -> [OneShotInfo]
- argsOneShots :: DmdSig -> Arity -> [[OneShotInfo]]
- saturatedByOneShots :: Int -> Demand -> Bool
- unboxDeeplyDmd :: Demand -> Demand
- data Divergence
- topDiv :: Divergence
- botDiv :: Divergence
- exnDiv :: Divergence
- lubDivergence :: Divergence -> Divergence -> Divergence
- isDeadEndDiv :: Divergence -> Bool
- data DmdEnv = DE {
- de_fvs :: !(VarEnv Demand)
- de_div :: !Divergence
- addVarDmdEnv :: DmdEnv -> Id -> Demand -> DmdEnv
- mkTermDmdEnv :: VarEnv Demand -> DmdEnv
- nopDmdEnv :: DmdEnv
- plusDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
- plusDmdEnvs :: [DmdEnv] -> DmdEnv
- reuseEnv :: DmdEnv -> DmdEnv
- data DmdType = DmdType {}
- dmdTypeDepth :: DmdType -> Arity
- nopDmdType :: DmdType
- botDmdType :: DmdType
- lubDmdType :: DmdType -> DmdType -> DmdType
- plusDmdType :: DmdType -> DmdEnv -> DmdType
- multDmdType :: Card -> DmdType -> DmdType
- discardArgDmds :: DmdType -> DmdEnv
- peelFV :: DmdType -> Var -> (DmdType, Demand)
- findIdDemand :: DmdType -> Var -> Demand
- addDemand :: Demand -> DmdType -> DmdType
- splitDmdTy :: DmdType -> (Demand, DmdType)
- deferAfterPreciseException :: DmdType -> DmdType
- newtype DmdSig = DmdSig DmdType
- mkDmdSigForArity :: Arity -> DmdType -> DmdSig
- mkClosedDmdSig :: [Demand] -> Divergence -> DmdSig
- mkVanillaDmdSig :: Arity -> Divergence -> DmdSig
- splitDmdSig :: DmdSig -> ([Demand], Divergence)
- dmdSigDmdEnv :: DmdSig -> DmdEnv
- hasDemandEnvSig :: DmdSig -> Bool
- nopSig :: DmdSig
- botSig :: DmdSig
- isNopSig :: DmdSig -> Bool
- isBottomingSig :: DmdSig -> Bool
- isDeadEndSig :: DmdSig -> Bool
- isDeadEndAppSig :: DmdSig -> Int -> Bool
- trimBoxityDmdSig :: DmdSig -> DmdSig
- transferArgBoxityDmdSig :: DmdSig -> DmdSig -> DmdSig
- prependArgsDmdSig :: Int -> DmdSig -> DmdSig
- etaConvertDmdSig :: Arity -> DmdSig -> DmdSig
- type DmdTransformer = SubDemand -> DmdType
- dmdTransformSig :: DmdSig -> DmdTransformer
- dmdTransformDataConSig :: [StrictnessMark] -> DmdTransformer
- dmdTransformDictSelSig :: DmdSig -> DmdTransformer
- data TypeShape
- trimToType :: Demand -> TypeShape -> Demand
- trimBoxity :: Demand -> Demand
- seqDemand :: Demand -> ()
- seqDemandList :: [Demand] -> ()
- seqDmdType :: DmdType -> ()
- seqDmdSig :: DmdSig -> ()
- zapUsageDemand :: Demand -> Demand
- zapDmdEnvSig :: DmdSig -> DmdSig
- zapUsedOnceDemand :: Demand -> Demand
- zapUsedOnceSig :: DmdSig -> DmdSig
Demands
Instances
Data Boxity # | |
Defined in Language.Haskell.Syntax.Basic Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Boxity -> c Boxity # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Boxity # toConstr :: Boxity -> Constr # dataTypeOf :: Boxity -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c Boxity) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Boxity) # gmapT :: (forall b. Data b => b -> b) -> Boxity -> Boxity # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Boxity -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Boxity -> r # gmapQ :: (forall d. Data d => d -> u) -> Boxity -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Boxity -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Boxity -> m Boxity # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Boxity -> m Boxity # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Boxity -> m Boxity # | |
Binary Boxity # | |
Outputable Boxity # | |
Defined in GHC.Types.Basic | |
Eq Boxity # | |
Describes an interval of evaluation cardinalities. See Note [Evaluation cardinalities] See Note [Bit vector representation for Card]
Bundled Patterns
pattern C_00 :: Card | Absent, {0}. Pretty-printed as A. |
pattern C_01 :: Card | Used at most once, {0,1}. Pretty-printed as M. |
pattern C_0N :: Card | Every possible cardinality; the top element, {0,1,n}. Pretty-printed as L. |
pattern C_10 :: Card | Bottom, {}. Pretty-printed as A. |
pattern C_11 :: Card | Strict and used once, {1}. Pretty-printed as 1. |
pattern C_1N :: Card | Strict and used (possibly) many times, {1,n}. Pretty-printed as S. |
type CardNonAbs = Card #
type CardNonOnce = Card #
A demand describes
- How many times a variable is evaluated, via a
Card
inality, and - How deep its value was evaluated in turn, via 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 demand1C(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 demandMC(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 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. 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 (:*)
.
Constructors
BotDmd | A bottoming demand, produced by a diverging function ( |
AbsDmd | An absent demand: Evaluated exactly 0 times ( |
Bundled Patterns
pattern (:*) :: HasDebugCallStack => Card -> SubDemand -> Demand |
Matching on this pattern synonym is a complete match.
If the matched demand was Call sites should consider whether they really want to look at the
|
Instances
Binary Demand # | |
Outputable Demand # | See Note [Demand notation] |
Defined in GHC.Types.Demand | |
Eq Demand # | |
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 !CardNonOnce | Polymorphic demand, the denoted thing is evaluated arbitrarily deep,
with the specified cardinality at every level. The
In Note [Demand notation]: We'll only see Why doesn't this constructor simply carry a |
Prod !Boxity ![Demand] |
|
mkProd :: Boxity -> [Demand] -> SubDemand #
A smart constructor for Prod
, applying rewrite rules along the semantic
equality Prod b [n :* Poly Boxed n, ...] === Poly b n
, simplifying to
Poly
SubDemand
s when possible. Examples:
- Rewrites
P(L,L)
(e.g., argumentsBoxed
,[L,L]
) toL
- Rewrites
!P(L!L,L!L)
(e.g., argumentsUnboxed
,[L!L,L!L]
) to!L
- Does not rewrite
P(1L)
,P(L!L)
,!P(L)
orP(L,A)
Algebra
Least upper bound
Plus
plusSubDmd :: SubDemand -> SubDemand -> SubDemand #
Multiply
multSubDmd :: Card -> SubDemand -> SubDemand #
Predicates on Card
inalities and Demand
s
isUsedOnce :: Card -> Bool #
True = upper bound is 1.
isUsedOnceDmd :: Demand -> Bool #
Is the value used at most once?
isStrUsedDmd :: Demand -> Bool #
Not absent and used strictly. See Note [Strict demands]
isStrictDmd :: Demand -> Bool #
Contrast with isStrictUsedDmd. See Note [Strict demands]
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.
onlyBoxedArguments :: DmdSig -> Bool #
True when the signature indicates all arguments are boxed
Special demands
Demands used in PrimOp signatures
lazyApply1Dmd :: Demand #
First argument of catch#: MC(1,L)
.
Evaluates its arg lazily, but then applies it exactly once to one argument.
lazyApply2Dmd :: Demand #
Second argument of catch#: MC(1,C(1,L))
.
Evaluates its arg lazily, but then applies it exactly once to two arguments.
strictOnceApply1Dmd :: Demand #
First argument of 'GHC.Exts.maskAsyncExceptions#': 1C(1,L)
.
Called exactly once.
strictManyApply1Dmd :: Demand #
First argument of 'GHC.Exts.atomically#': SC(S,L)
.
Called at least once, possibly many times.
Other Demand
operations
oneifyCard :: Card -> Card #
Intersect with [0,1].
strictifyDmd :: Demand -> Demand #
Make a Demand
evaluated at-least-once (e.g. strict).
strictifyDictDmd :: Type -> Demand -> Demand #
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.
peelCallDmd :: SubDemand -> (Card, SubDemand) #
Peels one call level from the sub-demand, and also returns how many times we entered the lambda body.
mkCalledOnceDmd :: SubDemand -> SubDemand #
Wraps the SubDemand
with a one-shot call demand: d
-> C(1,d)
.
mkCalledOnceDmds :: Arity -> SubDemand -> SubDemand #
mkCalledOnceDmds n d
returns C(1,C1...C(1,d))
where there are n
C1
's.
mkWorkerDemand :: Int -> Demand #
subDemandIfEvaluated :: Demand -> SubDemand #
Extracting one-shot information
Arguments
:: Demand | depending on saturation |
-> [OneShotInfo] |
See Note [Computing one-shot info]
argsOneShots :: DmdSig -> Arity -> [[OneShotInfo]] #
See Note [Computing one-shot info]
saturatedByOneShots :: Int -> Demand -> Bool #
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
unboxDeeplyDmd :: Demand -> Demand #
Divergence
data Divergence #
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.
Constructors
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 Methods put_ :: BinHandle -> Divergence -> IO () # put :: BinHandle -> Divergence -> IO (Bin Divergence) # get :: BinHandle -> IO Divergence # | |
Outputable Divergence # | |
Defined in GHC.Types.Demand Methods ppr :: Divergence -> SDoc # | |
Eq Divergence # | |
Defined in GHC.Types.Demand |
topDiv :: Divergence #
botDiv :: Divergence #
exnDiv :: Divergence #
lubDivergence :: Divergence -> Divergence -> Divergence #
isDeadEndDiv :: Divergence -> Bool #
True if the Divergence
indicates that evaluation will not return.
See Note [Dead ends].
Demand environments
Captures the result of an evaluation of an expression, by
- Listing how the free variables of that expression have been evaluted
(
de_fvs
) - Saying whether or not evaluation would surely diverge (
de_div
)
See Note [Demand env Equality].
Instances
Binary DmdEnv # | |
Outputable DmdEnv # | |
Defined in GHC.Types.Demand | |
Eq DmdEnv # | |
mkTermDmdEnv :: VarEnv Demand -> DmdEnv #
Build a potentially terminating DmdEnv
from a finite map that says what
has been evaluated so far
plusDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv #
plusDmdEnvs :: [DmdEnv] -> DmdEnv #
DmdEnv
is a monoid via plusDmdEnv
and nopDmdEnv
; this is its msum
Demand types
Characterises how an expression
Constructors
DmdType | |
Instances
Binary DmdType # | |
Outputable DmdType # | |
Defined in GHC.Types.Demand | |
Eq DmdType # | See Note [Demand env Equality]. |
dmdTypeDepth :: DmdType -> Arity #
Algebra
nopDmdType :: DmdType #
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 #
lubDmdType :: DmdType -> DmdType -> DmdType #
Compute the least upper bound of two DmdType
s elicited /by the same
incoming demand/!
plusDmdType :: DmdType -> DmdEnv -> DmdType #
multDmdType :: Card -> DmdType -> DmdType #
discardArgDmds :: DmdType -> DmdEnv #
Other operations
findIdDemand :: DmdType -> Var -> Demand #
splitDmdTy :: DmdType -> (Demand, DmdType) #
deferAfterPreciseException :: DmdType -> DmdType #
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
The 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]
Instances
Binary DmdSig # | |
Outputable DmdSig # | |
Defined in GHC.Types.Demand | |
Eq DmdSig # | |
mkDmdSigForArity :: Arity -> DmdType -> DmdSig #
mkClosedDmdSig :: [Demand] -> Divergence -> DmdSig #
mkVanillaDmdSig :: Arity -> Divergence -> DmdSig #
splitDmdSig :: DmdSig -> ([Demand], Divergence) #
dmdSigDmdEnv :: DmdSig -> DmdEnv #
hasDemandEnvSig :: DmdSig -> Bool #
isBottomingSig :: DmdSig -> Bool #
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].
isDeadEndSig :: DmdSig -> Bool #
True if the signature diverges or throws an exception in a saturated call. See Note [Dead ends].
isDeadEndAppSig :: DmdSig -> Int -> Bool #
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].
trimBoxityDmdSig :: DmdSig -> DmdSig #
transferArgBoxityDmdSig :: DmdSig -> DmdSig -> DmdSig #
Handling arity adjustments
prependArgsDmdSig :: Int -> DmdSig -> DmdSig #
Add extra (topDmd
) arguments to a strictness signature.
In contrast to etaConvertDmdSig
, this prepends additional argument
demands. This is used by FloatOut.
etaConvertDmdSig :: Arity -> DmdSig -> DmdSig #
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
type DmdTransformer = SubDemand -> DmdType #
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 DmdSig] and Note [What are demand signatures?].
dmdTransformSig :: DmdSig -> DmdTransformer #
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.
dmdTransformDataConSig :: [StrictnessMark] -> DmdTransformer #
A special DmdTransformer
for data constructors that feeds product
demands into the constructor arguments.
dmdTransformDictSelSig :: DmdSig -> DmdTransformer #
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
Instances
Outputable TypeShape # | |
Defined in GHC.Types.Demand |
trimToType :: Demand -> TypeShape -> Demand #
trimBoxity :: Demand -> Demand #
Drop all boxity
seq
ing stuff
seqDemandList :: [Demand] -> () #
seqDmdType :: DmdType -> () #
Zapping usage information
zapUsageDemand :: Demand -> Demand #
zapDmdEnvSig :: DmdSig -> DmdSig #
Remove the demand environment from the signature.
zapUsedOnceDemand :: Demand -> Demand #
Remove all `C_01 :*` info (but not CM
sub-demands) from the demand
zapUsedOnceSig :: DmdSig -> DmdSig #
Remove all `C_01 :*` info (but not CM
sub-demands) from the strictness
signature