summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/GHC/Core/Opt/DmdAnal.hs25
-rw-r--r--compiler/GHC/Types/Demand.hs512
2 files changed, 351 insertions, 186 deletions
diff --git a/compiler/GHC/Core/Opt/DmdAnal.hs b/compiler/GHC/Core/Opt/DmdAnal.hs
index bad3234ca9..5f209701a9 100644
--- a/compiler/GHC/Core/Opt/DmdAnal.hs
+++ b/compiler/GHC/Core/Opt/DmdAnal.hs
@@ -43,7 +43,8 @@ import GHC.Builtin.PrimOps
import GHC.Builtin.Types.Prim ( realWorldStatePrimTy )
import GHC.Types.Unique.Set
--- import GHC.Driver.Ppr
+import GHC.Utils.Trace
+_ = pprTrace -- Tired of commenting out the import all the time
{-
************************************************************************
@@ -340,11 +341,12 @@ dmdAnalStar :: AnalEnv
-> Demand -- This one takes a *Demand*
-> CoreExpr -- Should obey the let/app invariant
-> (PlusDmdArg, CoreExpr)
-dmdAnalStar env (n :* cd) e
- | WithDmdType dmd_ty e' <- dmdAnal env cd e
+dmdAnalStar env (n :* sd) e
+ -- NB: (:*) expands AbsDmd and BotDmd as needed
+ -- See Note [Analysing with absent demand]
+ | WithDmdType dmd_ty e' <- dmdAnal env sd e
= assertPpr (not (isUnliftedType (exprType e)) || exprOkForSpeculation e) (ppr e)
-- The argument 'e' should satisfy the let/app invariant
- -- See Note [Analysing with absent demand] in GHC.Types.Demand
(toPlusDmdArg $ multDmdType n dmd_ty, e')
-- Main Demand Analsysis machinery
@@ -427,7 +429,7 @@ dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt bndrs rhs])
WithDmdType alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr
-- Evaluation cardinality on the case binder is irrelevant and a no-op.
-- What matters is its nested sub-demand!
- (_ :* case_bndr_sd) = case_bndr_dmd
+ (_ :* case_bndr_sd) = case_bndr_dmd
-- Compute demand on the scrutinee
-- FORCE the result, otherwise thunks will end up retaining the
-- whole DmdEnv
@@ -548,7 +550,7 @@ dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs)
, WithDmdType alt_ty dmds <- findBndrsDmds env rhs_ty bndrs
, let (_ :* case_bndr_sd) = findIdDemand alt_ty case_bndr
-- See Note [Demand on scrutinee of a product case]
- id_dmds = addCaseBndrDmd case_bndr_sd dmds
+ id_dmds = addCaseBndrDmd case_bndr_sd dmds
-- Do not put a thunk into the Alt
!new_ids = setBndrsDemandInfo bndrs id_dmds
= WithDmdType alt_ty (Alt con new_ids rhs')
@@ -557,7 +559,7 @@ dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs)
Note [Analysing with absent demand]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose we analyse an expression with demand A. The "A" means
-"absent", so this expression will never be needed. What should happen?
+"absent", so this expression will never be needed. What should happen?
There are several wrinkles:
* We *do* want to analyse the expression regardless.
@@ -566,6 +568,15 @@ There are several wrinkles:
But we can post-process the results to ignore all the usage
demands coming back. This is done by multDmdType.
+* Nevertheless, which sub-demand should we pick for analysis?
+ Since the demand was absent, any would do. Worker/wrapper will replace
+ absent bindings with an absent filler anyway, so annotations in the RHS
+ of an absent binding don't matter much.
+ Picking 'botSubDmd' would be the most useful, but would also look a bit
+ misleading in the Core output of DmdAnal, because all nested annotations would
+ be bottoming. Better pick 'seqSubDmd', so that we annotate many of those
+ nested bindings with A themselves.
+
* In a previous incarnation of GHC we needed to be extra careful in the
case of an *unlifted type*, because unlifted values are evaluated
even if they are not used. Example (see #9254):
diff --git a/compiler/GHC/Types/Demand.hs b/compiler/GHC/Types/Demand.hs
index e8ea103705..4b9a04b9fb 100644
--- a/compiler/GHC/Types/Demand.hs
+++ b/compiler/GHC/Types/Demand.hs
@@ -1,5 +1,6 @@
-
{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE BinaryLiterals #-}
+{-# LANGUAGE PatternSynonyms #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
@@ -15,7 +16,9 @@
-- Lays out the abstract domain for "GHC.Core.Opt.DmdAnal".
module GHC.Types.Demand (
-- * Demands
- Card(..), Demand(..), SubDemand(Prod), mkProd, viewProd,
+ Card(C_00, C_01, C_0N, C_10, C_11, C_1N), CardNonAbs, CardNonOnce,
+ Demand(AbsDmd, BotDmd, (:*)),
+ SubDemand(Prod), mkProd, viewProd,
-- ** Algebra
absDmd, topDmd, botDmd, seqDmd, topSubDmd,
-- *** Least upper bound
@@ -95,6 +98,12 @@ import GHC.Utils.Binary
import GHC.Utils.Misc
import GHC.Utils.Outputable
import GHC.Utils.Panic
+import GHC.Utils.Panic.Plain
+
+import Data.Function
+
+import GHC.Utils.Trace
+_ = pprTrace -- Tired of commenting out the import all the time
{-
************************************************************************
@@ -106,17 +115,15 @@ import GHC.Utils.Panic
{- Note [Evaluation cardinalities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The demand analyser uses an /evaluation cardinality/ of type Card,
-to specify how many times a term is evaluated. A cardinality C_lu
-represents an /interval/ [l..u], meaning
- C_lu means evaluated /at least/ 'l' times and
- /at most/ 'u' times
+The demand analyser uses an (abstraction of) /evaluation cardinality/ of type
+Card, to specify how many times a term is evaluated. A Card C_lu
+represents an /interval/ of possible cardinalities [l..u], meaning
-* The lower bound corresponds to /strictness/
+* Evaluated /at least/ 'l' times (strictness).
Hence 'l' is either 0 (lazy)
or 1 (strict)
-* The upper bound corresponds to /usage/
+* Evaluated /at most/ 'u' times (usage).
Hence 'u' is either 0 (not used at all),
or 1 (used at most once)
or n (no information)
@@ -127,42 +134,156 @@ Usually l<=u, but we also have C_10, the interval [1,0], the empty interval,
denoting the empty set. This is the bottom element of the lattice.
See Note [Demand notation] for the notation we use for each of the constructors.
+
+Note [Bit vector representation for Card]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+While the 6 inhabitants of Card admit an efficient representation as an
+enumeration, implementing operations such as lubCard, plusCard and multCard
+leads to unreasonably bloated code. This was the old defn for lubCard, for
+example:
+
+ -- Handle C_10 (bot)
+ lubCard C_10 n = n -- bot
+ lubCard n C_10 = n -- bot
+ -- Handle C_0N (top)
+ lubCard C_0N _ = C_0N -- top
+ lubCard _ C_0N = C_0N -- top
+ -- Handle C_11
+ lubCard C_00 C_11 = C_01 -- {0} ∪ {1} = {0,1}
+ lubCard C_11 C_00 = C_01 -- {0} ∪ {1} = {0,1}
+ lubCard C_11 n = n -- {1} is a subset of all other intervals
+ lubCard n C_11 = n -- {1} is a subset of all other intervals
+ -- Handle C_1N
+ lubCard C_1N C_1N = C_1N -- reflexivity
+ lubCard _ C_1N = C_0N -- {0} ∪ {1,n} = top
+ lubCard C_1N _ = C_0N -- {0} ∪ {1,n} = top
+ -- Handle C_01
+ lubCard C_01 _ = C_01 -- {0} ∪ {0,1} = {0,1}
+ lubCard _ C_01 = C_01 -- {0} ∪ {0,1} = {0,1}
+ -- Handle C_00
+ lubCard C_00 C_00 = C_00 -- reflexivity
+
+There's a much more compact way to encode these operations if Card is
+represented not as distinctly denoted intervals, but as the subset of the set
+of all cardinalities {0,1,n} instead. We represent such a subset as a bit vector
+of length 3 (which fits in an Int). That's actually pretty common for such
+powerset lattices.
+There's one bit per denoted cardinality that is set iff that cardinality is part
+of the denoted set, with n being the most significand bit (index 2) and 0 being
+represented by the least significand bit (index 0).
+
+How does that help? Well, for one, lubCard just becomes
+
+ lubCard (Card a) (Card b) = Card (a .|. b)
+
+The other operations, 'plusCard' and 'multCard', become significantly more
+tricky, but immensely more compact. It's all straight-line code with a few bit
+twiddling instructions now!
+
+Note [Algebraic specification for plusCard and multCard]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The representation change in Note [Bit vector representation for Card] admits
+very dense definitions of 'plusCard' and 'multCard' in terms of bit twiddling,
+but the connection to the algebraic operations they implement is lost.
+It's helpful to have a written specification of what 'plusCard' and 'multCard'
+here that says what they should compute.
+
+ * plusCard: a@[l1,u1] + b@[l2,u2] = r@[l1+l2,u1+u2].
+ - In terms of sets, 0 ∈ r iff 0 ∈ a and 0 ∈ b.
+ Examples: set in C_00 + C_00, C_01 + C_0N, but not in C_10 + C_00
+ - In terms of sets, 1 ∈ r iff 1 ∈ a or 1 ∈ b.
+ Examples: set in C_01 + C_00, C_0N + C_0N, but not in C_10 + C_00
+ - In terms of sets, n ∈ r iff n ∈ a or n ∈ b, or (1 ∈ a and 1 ∈ b),
+ so not unlike add with carry.
+ Examples: set in C_01 + C_01, C_01 + C_0N, but not in C_10 + C_01
+ - Handy special cases:
+ o 'plusCard C_10' bumps up the strictness of its argument, just like
+ 'lubCard C_00' lazifies it, without touching upper bounds.
+ o Similarly, 'plusCard C_0N' discards usage information
+ (incl. absence) but leaves strictness alone.
+
+ * multCard: a@[l1,u1] * b@[l2,u2] = r@[l1*l2,u1*u2].
+ - In terms of sets, 0 ∈ r iff 0 ∈ a or 0 ∈ b.
+ Examples: set in C_00 * C_10, C_01 * C_1N, but not in C_10 * C_1N
+ - In terms of sets, 1 ∈ r iff 1 ∈ a and 1 ∈ b.
+ Examples: set in C_01 * C_01, C_01 * C_1N, but not in C_11 * C_10
+ - In terms of sets, n ∈ r iff 1 ∈ r and (n ∈ a or n ∈ b).
+ Examples: set in C_1N * C_01, C_1N * C_0N, but not in C_10 * C_1N
+ - Handy special cases:
+ o 'multCard C_1N c' is the same as 'plusCard c c' and
+ drops used-once info. But unlike 'plusCard C_0N', it leaves absence
+ and strictness.
+ o 'multCard C_01' drops strictness info, like 'lubCard C_00'.
+ o 'multCard C_0N' does both; it discards all strictness and used-once
+ info and retains only absence info.
-}
-- | Describes an interval of /evaluation cardinalities/.
-- See Note [Evaluation cardinalities]
-data Card
- = 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.
+-- See Note [Bit vector representation for Card]
+newtype Card = Card Int
deriving Eq
+-- | A subtype of 'Card' for which the upper bound is never 0 (no 'C_00' or
+-- 'C_10'). The only four inhabitants are 'C_01', 'C_0N', 'C_11', 'C_1N'.
+-- Membership can be tested with 'isCardNonAbs'.
+-- See 'D' and 'Call' for use sites and explanation.
+type CardNonAbs = Card
+
+-- | A subtype of 'Card' for which the upper bound is never 1 (no 'C_01' or
+-- 'C_11'). The only four inhabitants are 'C_00', 'C_0N', 'C_10', 'C_1N'.
+-- Membership can be tested with 'isCardNonOnce'.
+-- See 'Poly' for use sites and explanation.
+type CardNonOnce = Card
+
+-- | Absent, {0}. Pretty-printed as A.
+pattern C_00 :: Card
+pattern C_00 = Card 0b001
+-- | Bottom, {}. Pretty-printed as A.
+pattern C_10 :: Card
+pattern C_10 = Card 0b000
+-- | Strict and used once, {1}. Pretty-printed as 1.
+pattern C_11 :: Card
+pattern C_11 = Card 0b010
+-- | Used at most once, {0,1}. Pretty-printed as M.
+pattern C_01 :: Card
+pattern C_01 = Card 0b011
+-- | Strict and used (possibly) many times, {1,n}. Pretty-printed as S.
+pattern C_1N :: Card
+pattern C_1N = Card 0b110
+-- | Every possible cardinality; the top element, {0,1,n}. Pretty-printed as L.
+pattern C_0N :: Card
+pattern C_0N = Card 0b111
+
+{-# COMPLETE C_00, C_01, C_0N, C_10, C_11, C_1N :: Card #-}
+
_botCard, topCard :: Card
_botCard = C_10
topCard = C_0N
-- | True <=> lower bound is 1.
isStrict :: Card -> Bool
-isStrict C_10 = True
-isStrict C_11 = True
-isStrict C_1N = True
-isStrict _ = False
+-- See Note [Bit vector representation for Card]
+isStrict (Card c) = c .&. 0b001 == 0 -- simply check 0 bit is not set
-- | True <=> upper bound is 0.
isAbs :: Card -> Bool
-isAbs C_00 = True
-isAbs C_10 = True -- Bottom cardinality is also absent
-isAbs _ = False
+-- See Note [Bit vector representation for Card]
+isAbs (Card c) = c .&. 0b110 == 0 -- simply check 1 and n bit are not set
-- | True <=> upper bound is 1.
isUsedOnce :: Card -> Bool
-isUsedOnce C_0N = False
-isUsedOnce C_1N = False
-isUsedOnce _ = True
+-- See Note [Bit vector representation for Card]
+isUsedOnce (Card c) = c .&. 0b100 == 0 -- simply check n bit is not set
+
+-- | Is this a 'CardNonAbs'?
+isCardNonAbs :: Card -> Bool
+isCardNonAbs = not . isAbs
+
+-- | Is this a 'CardNonOnce'?
+isCardNonOnce :: Card -> Bool
+isCardNonOnce n = isAbs n || not (isUsedOnce n)
-- | Intersect with [0,1].
oneifyCard :: Card -> Card
@@ -172,62 +293,28 @@ oneifyCard c = c
-- | Denotes '∪' on 'Card'.
lubCard :: Card -> Card -> Card
--- Handle C_10 (bot)
-lubCard C_10 n = n -- bot
-lubCard n C_10 = n -- bot
--- Handle C_0N (top)
-lubCard C_0N _ = C_0N -- top
-lubCard _ C_0N = C_0N -- top
--- Handle C_11
-lubCard C_00 C_11 = C_01 -- {0} ∪ {1} = {0,1}
-lubCard C_11 C_00 = C_01 -- {0} ∪ {1} = {0,1}
-lubCard C_11 n = n -- {1} is a subset of all other intervals
-lubCard n C_11 = n -- {1} is a subset of all other intervals
--- Handle C_1N
-lubCard C_1N C_1N = C_1N -- reflexivity
-lubCard _ C_1N = C_0N -- {0} ∪ {1,n} = top
-lubCard C_1N _ = C_0N -- {0} ∪ {1,n} = top
--- Handle C_01
-lubCard C_01 _ = C_01 -- {0} ∪ {0,1} = {0,1}
-lubCard _ C_01 = C_01 -- {0} ∪ {0,1} = {0,1}
--- Handle C_00
-lubCard C_00 C_00 = C_00 -- reflexivity
-
--- | Denotes '+' on 'Card'.
+-- See Note [Bit vector representation for Card]
+lubCard (Card a) (Card b) = Card (a .|. b) -- main point of the bit-vector encoding!
+
+-- | Denotes '+' on lower and upper bounds of 'Card'.
plusCard :: Card -> Card -> Card
--- Handle C_00
-plusCard C_00 n = n -- {0}+n = n
-plusCard n C_00 = n -- {0}+n = n
--- Handle C_10
-plusCard C_10 C_01 = C_11 -- These follow by applying + to lower and upper
-plusCard C_10 C_0N = C_1N -- bounds individually
-plusCard C_10 n = n
-plusCard C_01 C_10 = C_11
-plusCard C_0N C_10 = C_1N
-plusCard n C_10 = n
--- Handle the rest (C_01, C_0N, C_11, C_1N)
-plusCard C_01 C_01 = C_0N -- The upper bound is at least 1, so upper bound of
-plusCard C_01 C_0N = C_0N -- the result must be 1+1 ~= N.
-plusCard C_0N C_01 = C_0N -- But for the lower bound we have 4 cases where
-plusCard C_0N C_0N = C_0N -- 0+0 ~= 0 (as opposed to 1), so we match on these.
-plusCard _ _ = C_1N -- Otherwise we return {1,n}
-
--- | Denotes '*' on 'Card'.
+-- See Note [Algebraic specification for plusCard and multCard]
+plusCard (Card a) (Card b)
+ = Card (bit0 .|. bit1 .|. bitN)
+ where
+ bit0 = (a .&. b) .&. 0b001
+ bit1 = (a .|. b) .&. 0b010
+ bitN = ((a .|. b) .|. shiftL (a .&. b) 1) .&. 0b100
+
+-- | Denotes '*' on lower and upper bounds of 'Card'.
multCard :: Card -> Card -> Card
--- Handle C_11 (neutral element)
-multCard C_11 c = c
-multCard c C_11 = c
--- Handle C_00 (annihilating element)
-multCard C_00 _ = C_00
-multCard _ C_00 = C_00
--- Handle C_10
-multCard C_10 c = if isStrict c then C_10 else C_00
-multCard c C_10 = if isStrict c then C_10 else C_00
--- Handle reflexive C_1N, C_01
-multCard C_1N C_1N = C_1N
-multCard C_01 C_01 = C_01
--- Handle C_0N and the rest (C_01, C_1N):
-multCard _ _ = C_0N
+-- See Note [Algebraic specification for plusCard and multCard]
+multCard (Card a) (Card b)
+ = Card (bit0 .|. bit1 .|. bitN)
+ where
+ bit0 = (a .|. b) .&. 0b001
+ bit1 = (a .&. b) .&. 0b010
+ bitN = (a .|. b) .&. shiftL bit1 1 .&. 0b100
{-
************************************************************************
@@ -261,11 +348,47 @@ multCard _ _ = C_0N
--
-- 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.
+-- 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 '(:*)'.
data Demand
- = !Card :* !SubDemand
+ = BotDmd
+ -- ^ A bottoming demand, produced by a diverging function, hence there is no
+ -- 'SubDemand' that describes how it was evaluated.
+ | AbsDmd
+ -- ^ An absent demand: Evaluated exactly 0 times ('C_00'), hence there is no
+ -- 'SubDemand' that describes how it was evaluated.
+ | D !CardNonAbs !SubDemand
+ -- ^ Don't use this internal data constructor; use '(:*)' instead.
deriving Eq
+-- | Only meant to be used in the pattern synonym below!
+viewDmdPair :: Demand -> (Card, SubDemand)
+viewDmdPair BotDmd = (C_10, botSubDmd)
+viewDmdPair AbsDmd = (C_00, seqSubDmd)
+viewDmdPair (D n sd) = (n, sd)
+
+-- | @c :* sd@ is a demand that says \"evaluated @c@ times, and each time it
+-- was evaluated, it was 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.
+pattern (:*) :: HasDebugCallStack => Card -> SubDemand -> Demand
+pattern n :* sd <- (viewDmdPair -> (n, sd)) where
+ C_10 :* sd = BotDmd & assertPpr (sd == botSubDmd) (text "B /=" <+> ppr sd)
+ C_00 :* sd = AbsDmd & assertPpr (sd == seqSubDmd) (text "A /=" <+> ppr sd)
+ n :* sd = D n sd & assertPpr (isCardNonAbs n) (ppr n $$ ppr sd)
+{-# COMPLETE (:*) #-}
+
-- | A sub-demand describes an /evaluation context/, e.g. how deep the
-- denoted thing is evaluated. See 'Demand' for examples.
--
@@ -284,7 +407,7 @@ data Demand
-- See Note [Call demands are relative]
-- and Note [Demand notation].
data SubDemand
- = Poly !Card
+ = Poly !CardNonOnce
-- ^ Polymorphic demand, the denoted thing is evaluated arbitrarily deep,
-- with the specified cardinality at every level.
-- Expands to 'Call' via 'viewCall' and to 'Prod' via 'viewProd'.
@@ -293,15 +416,17 @@ data SubDemand
-- @Call n (Poly n)@. 'mkCall' and 'mkProd' do these rewrites.
--
-- In Note [Demand notation]: @L === P(L,L,...)@ and @L === CL(L)@,
- -- @1 === P(1,1,...)@ and @1 === C1(1)@, and so on.
+ -- @B === P(B,B,...)@ and @B === CB(B)@, and so on.
--
-- We only really use 'Poly' with 'C_10' (B), 'C_00' (A), 'C_0N' (L) and
- -- sometimes 'C_1N' (S), but it's simpler to treat it uniformly than to
- -- have a special constructor for each of the three cases.
- | Call !Card !SubDemand
+ -- sometimes 'C_1N' (S), hence 'CardNonOnce'.
+ | Call !CardNonAbs !SubDemand
-- ^ @Call n sd@ describes the evaluation context of @n@ function
-- applications, where every individual result is evaluated according to @sd@.
- -- @sd@ is /relative/ to a single call, cf. Note [Call demands are relative].
+ -- @sd@ is /relative/ to a single call, see Note [Call demands are relative].
+ -- That Note also explains why it doesn't make sense for @n@ to be absent,
+ -- hence we forbid it with 'CardNonAbs'. Absent call demands can still be
+ -- expressed with 'Poly'.
-- Used only for values of function type. Use the smart constructor 'mkCall'
-- whenever possible!
| Prod ![Demand]
@@ -310,25 +435,22 @@ data SubDemand
-- evaluated according to @ds@.
deriving Eq
-poly00, poly01, poly0N, poly11, poly1N, poly10 :: SubDemand
+poly00, poly0N, poly1N, poly10 :: SubDemand
topSubDmd, botSubDmd, seqSubDmd :: SubDemand
poly00 = Poly C_00
-poly01 = Poly C_01
poly0N = Poly C_0N
-poly11 = Poly C_11
poly1N = Poly C_1N
poly10 = Poly C_10
topSubDmd = poly0N
botSubDmd = poly10
seqSubDmd = poly00
-polyDmd :: Card -> Demand
-polyDmd C_00 = C_00 :* poly00
-polyDmd C_01 = C_01 :* poly01
+polyDmd :: CardNonOnce -> Demand
+polyDmd C_00 = AbsDmd
+polyDmd C_10 = BotDmd
polyDmd C_0N = C_0N :* poly0N
-polyDmd C_11 = C_11 :* poly11
polyDmd C_1N = C_1N :* poly1N
-polyDmd C_10 = C_10 :* poly10
+polyDmd c = pprPanic "non-once Card" (ppr c)
-- | A smart constructor for 'Prod', applying rewrite rules along the semantic
-- equality @Prod [polyDmd n, ...] === polyDmd n@, simplifying to 'Poly'
@@ -336,15 +458,12 @@ polyDmd C_10 = C_10 :* poly10
-- polymorphic demand will never unbox.
mkProd :: [Demand] -> SubDemand
mkProd [] = seqSubDmd
-mkProd ds@(n:*sd : _)
- | want_to_simplify n, all (== polyDmd n) ds = sd
- | otherwise = Prod ds
- where
- -- We only want to simplify absent and bottom demands and unbox the others.
- -- See also Note [L should win] and Note [Don't optimise LP(L,L,...) to L].
- want_to_simplify C_00 = True
- want_to_simplify C_10 = True
- want_to_simplify _ = False
+-- We only want to simplify absent and bottom demands and unbox the others.
+-- See also Note [L should win] and Note [Don't optimise LP(L,L,...) to L].
+mkProd ds
+ | all (== AbsDmd) ds = seqSubDmd
+ | all (== BotDmd) ds = botSubDmd
+ | otherwise = Prod ds
-- | @viewProd n sd@ interprets @sd@ as a 'Prod' of arity @n@, expanding 'Poly'
-- demands as necessary.
@@ -363,37 +482,36 @@ viewProd _ _ = Nothing
-- | A smart constructor for 'Call', applying rewrite rules along the semantic
-- equality @Call n (Poly n) === Poly n@, simplifying to 'Poly' 'SubDemand's
-- when possible.
-mkCall :: Card -> SubDemand -> SubDemand
-mkCall n cd@(Poly m) | n == m = cd
-mkCall n cd = Call n cd
-
--- | @viewCall sd@ interprets @sd@ as a 'Call', expanding 'Poly' demands as
--- necessary.
-viewCall :: SubDemand -> Maybe (Card, SubDemand)
-viewCall (Call n sd) = Just (n, sd)
-viewCall sd@(Poly card) = Just (card, sd)
-viewCall _ = Nothing
+mkCall :: CardNonAbs -> SubDemand -> SubDemand
+mkCall C_1N sd@(Poly C_1N) = sd
+mkCall C_0N sd@(Poly C_0N) = sd
+mkCall n cd = assertPpr (isCardNonAbs n) (ppr n $$ ppr cd) $
+ Call n cd
topDmd, absDmd, botDmd, seqDmd :: Demand
topDmd = polyDmd C_0N
-absDmd = polyDmd C_00
-botDmd = polyDmd C_10
+absDmd = AbsDmd
+botDmd = BotDmd
seqDmd = C_11 :* seqSubDmd
-- | Denotes '∪' on 'SubDemand'.
lubSubDmd :: SubDemand -> SubDemand -> SubDemand
+-- Handle botSubDmd (just an optimisation, the general case would do the same)
+lubSubDmd (Poly C_10) d2 = d2
+lubSubDmd d1 (Poly C_10) = d1
-- Handle Prod
-lubSubDmd (Prod ds1) (viewProd (length ds1) -> Just ds2) =
- Prod $ strictZipWith lubDmd ds2 ds1 -- try to fuse with ds2
+lubSubDmd (Prod ds1) (Poly n2) = Prod $ strictMap (lubDmd (polyDmd n2)) ds1
+lubSubDmd (Prod ds1) (Prod ds2)
+ | equalLength ds1 ds2 = Prod $ strictZipWith lubDmd ds1 ds2
-- Handle Call
-lubSubDmd (Call n1 d1) (viewCall -> Just (n2, d2))
+lubSubDmd (Call n1 sd1) (Poly n2)
-- See Note [Call demands are relative]
- | isAbs n1 = mkCall (lubCard n1 n2) (lubSubDmd botSubDmd d2)
- | isAbs n2 = mkCall (lubCard n1 n2) (lubSubDmd d1 botSubDmd)
- | otherwise = mkCall (lubCard n1 n2) (lubSubDmd d1 d2)
--- Handle Poly
+ | isAbs n2 = mkCall (lubCard n2 n1) sd1
+ | otherwise = mkCall (lubCard n2 n1) (lubSubDmd sd1 (Poly n2))
+lubSubDmd (Call n1 d1) (Call n2 d2)
+ | otherwise = mkCall (lubCard n1 n2) (lubSubDmd d1 d2)
+-- Handle Poly. Exploit reflexivity (so we'll match the Prod or Call cases again).
lubSubDmd (Poly n1) (Poly n2) = Poly (lubCard n1 n2)
--- Make use of reflexivity (so we'll match the Prod or Call cases again).
lubSubDmd sd1@Poly{} sd2 = lubSubDmd sd2 sd1
-- Otherwise (Call `lub` Prod) return Top
lubSubDmd _ _ = topSubDmd
@@ -404,50 +522,46 @@ lubDmd (n1 :* sd1) (n2 :* sd2) = lubCard n1 n2 :* lubSubDmd sd1 sd2
-- | Denotes '+' on 'SubDemand'.
plusSubDmd :: SubDemand -> SubDemand -> SubDemand
+-- Handle seqSubDmd (just an optimisation, the general case would do the same)
+plusSubDmd (Poly C_00) d2 = d2
+plusSubDmd d1 (Poly C_00) = d1
-- Handle Prod
-plusSubDmd (Prod ds1) (viewProd (length ds1) -> Just ds2) =
- Prod $ zipWith plusDmd ds2 ds1 -- try to fuse with ds2
+plusSubDmd (Prod ds1) (Poly n2) = Prod $ strictMap (plusDmd (polyDmd n2)) ds1
+plusSubDmd (Prod ds1) (Prod ds2)
+ | equalLength ds1 ds2 = Prod $ strictZipWith plusDmd ds1 ds2
-- Handle Call
-plusSubDmd (Call n1 d1) (viewCall -> Just (n2, d2))
+plusSubDmd (Call n1 d1) (Poly n2)
-- See Note [Call demands are relative]
- | isAbs n1 = mkCall (plusCard n1 n2) (lubSubDmd botSubDmd d2)
- | isAbs n2 = mkCall (plusCard n1 n2) (lubSubDmd d1 botSubDmd)
- | otherwise = mkCall (plusCard n1 n2) (lubSubDmd d1 d2)
--- Handle Poly
+ | isAbs n2 = mkCall (plusCard n2 n1) d1
+ | otherwise = mkCall (plusCard n2 n1) (lubSubDmd d1 (Poly n2))
+plusSubDmd (Call n1 d1) (Call n2 d2)
+ | otherwise = mkCall (plusCard n1 n2) (lubSubDmd d1 d2)
+-- Handle Poly. Exploit (so we'll match the Prod or Call cases again).
plusSubDmd (Poly n1) (Poly n2) = Poly (plusCard n1 n2)
--- Make use of reflexivity (so we'll match the Prod or Call cases again).
plusSubDmd sd1@Poly{} sd2 = plusSubDmd sd2 sd1
-- Otherwise (Call `lub` Prod) return Top
-plusSubDmd _ _ = topSubDmd
+plusSubDmd _ _ = topSubDmd
-- | Denotes '+' on 'Demand'.
plusDmd :: Demand -> Demand -> Demand
plusDmd (n1 :* sd1) (n2 :* sd2) = plusCard n1 n2 :* plusSubDmd sd1 sd2
--- | The trivial cases of the @mult*@ functions.
--- If @multTrivial n abs a = ma@, we have the following outcomes
--- depending on @n@:
---
--- * 'C_11' => multiply by one, @ma = Just a@
--- * 'C_00', 'C_10' (e.g. @'isAbs' n@) => return the absent thing,
--- @ma = Just abs@
--- * Otherwise ('C_01', 'C_*N') it's not a trivial case, @ma = Nothing@.
-multTrivial :: Card -> a -> a -> Maybe a
-multTrivial C_11 _ a = Just a
-multTrivial n abs _ | isAbs n = Just abs
-multTrivial _ _ _ = Nothing
-
multSubDmd :: Card -> SubDemand -> SubDemand
-multSubDmd n sd
- | Just sd' <- multTrivial n seqSubDmd sd = sd'
-multSubDmd n (Poly n') = Poly (multCard n n')
-multSubDmd n (Call n' sd) = mkCall (multCard n n') sd -- See Note [Call demands are relative]
-multSubDmd n (Prod ds) = Prod (map (multDmd n) ds)
+multSubDmd C_11 sd = sd
+multSubDmd C_00 _ = seqSubDmd
+multSubDmd C_10 (Poly n) = if isStrict n then botSubDmd else seqSubDmd
+multSubDmd C_10 (Call n _) = if isStrict n then botSubDmd else seqSubDmd
+multSubDmd n (Poly m) = Poly (multCard n m)
+multSubDmd n (Call n' sd) = mkCall (multCard n n') sd -- See Note [Call demands are relative]
+multSubDmd n (Prod ds) = Prod (strictMap (multDmd n) ds)
multDmd :: Card -> Demand -> Demand
-multDmd n dmd
- | Just dmd' <- multTrivial n absDmd dmd = dmd'
-multDmd n (m :* dmd) = multCard n m :* multSubDmd n dmd
+-- The first two lines compute the same result as the last line, but won't
+-- trigger the assertion in `:*` for input like `multDmd B 1L`, which would call
+-- `B :* A`. We want to return `B` in these cases.
+multDmd C_10 (n :* _) = if isStrict n then BotDmd else AbsDmd
+multDmd n (C_10 :* _) = if isStrict n then BotDmd else AbsDmd
+multDmd n (m :* sd) = multCard n m :* multSubDmd n sd
-- | Used to suppress pretty-printing of an uninformative demand
isTopDmd :: Demand -> Bool
@@ -466,7 +580,7 @@ isStrUsedDmd (n :* _) = isStrict n && not (isAbs n)
isSeqDmd :: Demand -> Bool
isSeqDmd (C_11 :* sd) = sd == seqSubDmd
-isSeqDmd (C_1N :* sd) = sd == seqSubDmd -- I wonder if we need this case.
+isSeqDmd (C_1N :* sd) = sd == seqSubDmd
isSeqDmd _ = False
-- | Is the value used at most once?
@@ -481,11 +595,14 @@ isWeakDmd dmd@(n :* _) = not (isStrict n) && is_plus_idem_dmd dmd
where
-- @is_plus_idem_* thing@ checks whether @thing `plus` thing = thing@,
-- e.g. if @thing@ is idempotent wrt. to @plus@.
- is_plus_idem_card c = plusCard c c == c
+ -- is_plus_idem_card n = plusCard n n == n
+ is_plus_idem_card = isCardNonOnce
-- is_plus_idem_dmd dmd = plusDmd dmd dmd == dmd
+ is_plus_idem_dmd AbsDmd = True
+ is_plus_idem_dmd BotDmd = True
is_plus_idem_dmd (n :* sd) = is_plus_idem_card n && is_plus_idem_sub_dmd sd
-- is_plus_idem_sub_dmd sd = plusSubDmd sd sd == sd
- is_plus_idem_sub_dmd (Poly n) = is_plus_idem_card n
+ is_plus_idem_sub_dmd (Poly n) = assert (isCardNonOnce n) True
is_plus_idem_sub_dmd (Prod ds) = all is_plus_idem_dmd ds
is_plus_idem_sub_dmd (Call n _) = is_plus_idem_card n -- See Note [Call demands are relative]
@@ -514,10 +631,14 @@ lazyApply2Dmd = C_01 :* mkCall C_01 (mkCall C_11 topSubDmd)
-- | Make a 'Demand' evaluated at-most-once.
oneifyDmd :: Demand -> Demand
+oneifyDmd AbsDmd = AbsDmd
+oneifyDmd BotDmd = BotDmd
oneifyDmd (n :* sd) = oneifyCard n :* sd
-- | Make a 'Demand' evaluated at-least-once (e.g. strict).
strictifyDmd :: Demand -> Demand
+strictifyDmd AbsDmd = BotDmd
+strictifyDmd BotDmd = BotDmd
strictifyDmd (n :* sd) = plusCard C_10 n :* sd
-- | If the argument is a used non-newtype dictionary, give it strict demand.
@@ -557,6 +678,13 @@ mkCalledOnceDmd sd = mkCall C_11 sd
mkCalledOnceDmds :: Arity -> SubDemand -> SubDemand
mkCalledOnceDmds arity sd = iterate mkCalledOnceDmd sd !! arity
+-- | @viewCall sd@ interprets @sd@ as a 'Call', expanding 'TopSubDmd' and
+-- 'SeqSubDmd' as necessary.
+viewCall :: SubDemand -> Maybe (Card, SubDemand)
+viewCall (Call n sd) = Just (n :: Card, sd)
+viewCall (Poly n) = Just (n :: Card, Poly n)
+viewCall _ = Nothing
+
-- | Peels one call level from the sub-demand, and also returns how many
-- times we entered the lambda body.
peelCallDmd :: SubDemand -> (Card, SubDemand)
@@ -578,15 +706,14 @@ mkWorkerDemand n = C_01 :* go n
where go 0 = topSubDmd
go n = Call C_01 $ go (n-1)
+-- | Precondition: The SubDemand is not a Call
addCaseBndrDmd :: SubDemand -- On the case binder
-> [Demand] -- On the components of the constructor
-> [Demand] -- Final demands for the components of the constructor
-addCaseBndrDmd (Poly n) alt_dmds
- | isAbs n = alt_dmds
--- See Note [Demand on case-alternative binders]
-addCaseBndrDmd sd alt_dmds = zipWith plusDmd ds alt_dmds -- fuse ds!
- where
- Just ds = viewProd (length alt_dmds) sd -- Guaranteed not to be a call
+addCaseBndrDmd sd alt_dmds
+ -- See Note [Demand on case-alternative binders]
+ | Prod ds <- plusSubDmd sd (Prod alt_dmds) = ds
+ | otherwise = alt_dmds
argsOneShots :: DmdSig -> Arity -> [[OneShotInfo]]
-- ^ See Note [Computing one-shot info]
@@ -606,6 +733,9 @@ argsOneShots (DmdSig (DmdType _ arg_ds _)) n_val_args
argOneShots :: Demand -- ^ depending on saturation
-> [OneShotInfo]
-- ^ See Note [Computing one-shot info]
+argOneShots AbsDmd = [] -- This defn conflicts with 'saturatedByOneShots',
+argOneShots BotDmd = [] -- according to which we should return
+ -- @repeat OneShotLam@ here...
argOneShots (_ :* sd) = go sd -- See Note [Call demands are relative]
where
go (Call n sd)
@@ -619,6 +749,8 @@ argOneShots (_ :* sd) = go sd -- See Note [Call demands are relative]
-- There are at least n nested CM(..) calls.
-- See Note [Demand on the worker] in GHC.Core.Opt.WorkWrap
saturatedByOneShots :: Int -> Demand -> Bool
+saturatedByOneShots _ AbsDmd = True
+saturatedByOneShots _ BotDmd = True
saturatedByOneShots n (_ :* sd) = isUsedOnce (peelManyCalls n sd)
{- Note [Strict demands]
@@ -678,8 +810,8 @@ is hurt and we can assume that the nested demand is 'botSubDmd'. That ensures
that @g@ above actually gets the @1P(L)@ demand on its second pair component,
rather than the lazy @MP(L)@ if we 'lub'bed with an absent demand.
-Demand on case-alternative binders]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note [Demand on case-alternative binders]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The demand on a binder in a case alternative comes
(a) From the demand on the binder itself
(b) From the demand on the case binder
@@ -1086,9 +1218,9 @@ emptyDmdEnv :: DmdEnv
emptyDmdEnv = emptyVarEnv
multDmdEnv :: Card -> DmdEnv -> DmdEnv
-multDmdEnv n env
- | Just env' <- multTrivial n emptyDmdEnv env = env'
- | otherwise = mapVarEnv (multDmd n) env
+multDmdEnv C_11 env = env
+multDmdEnv C_00 _ = emptyDmdEnv
+multDmdEnv n env = mapVarEnv (multDmd n) env
reuseEnv :: DmdEnv -> DmdEnv
reuseEnv = multDmdEnv C_1N
@@ -1539,9 +1671,9 @@ dmdTransformDataConSig arity sd = case go arity sd of
Just dmds -> DmdType emptyDmdEnv dmds topDiv
Nothing -> nopDmdType -- Not saturated
where
- go 0 sd = viewProd arity sd
- go n (viewCall -> Just (C_11, sd)) = go (n-1) sd -- strict calls only!
- go _ _ = Nothing
+ go 0 sd = viewProd arity sd
+ go n (Call C_11 sd) = go (n-1) sd -- strict calls only!
+ go _ _ = Nothing
-- | A special 'DmdTransformer' for dictionary selectors that feeds the demand
-- on the result into the indicated dictionary component (if saturated).
@@ -1680,12 +1812,14 @@ kill_usage_card kfs C_11 | kf_used_once kfs = C_1N
kill_usage_card _ n = n
kill_usage :: KillFlags -> Demand -> Demand
+kill_usage _ AbsDmd = AbsDmd
+kill_usage _ BotDmd = BotDmd
kill_usage kfs (n :* sd) = kill_usage_card kfs n :* kill_usage_sd kfs sd
kill_usage_sd :: KillFlags -> SubDemand -> SubDemand
kill_usage_sd kfs (Call n sd)
| kf_called_once kfs = mkCall (lubCard C_1N n) (kill_usage_sd kfs sd)
- | otherwise = mkCall n (kill_usage_sd kfs sd)
+ | otherwise = mkCall n (kill_usage_sd kfs sd)
kill_usage_sd kfs (Prod ds) = Prod (map (kill_usage kfs) ds)
kill_usage_sd _ sd = sd
@@ -1704,6 +1838,8 @@ data TypeShape -- See Note [Trimming a demand to a type]
trimToType :: Demand -> TypeShape -> Demand
-- See Note [Trimming a demand to a type] in GHC.Core.Opt.DmdAnal
+trimToType AbsDmd _ = AbsDmd
+trimToType BotDmd _ = BotDmd
trimToType (n :* sd) ts
= n :* go sd ts
where
@@ -1722,6 +1858,8 @@ trimToType (n :* sd) ts
-}
seqDemand :: Demand -> ()
+seqDemand AbsDmd = ()
+seqDemand BotDmd = ()
seqDemand (_ :* sd) = seqSubDemand sd
seqSubDemand :: SubDemand -> ()
@@ -1750,6 +1888,15 @@ seqDmdSig (DmdSig ty) = seqDmdType ty
************************************************************************
-}
+-- Just for debugging purposes.
+instance Show Card where
+ show C_00 = "C_00"
+ show C_01 = "C_01"
+ show C_0N = "C_0N"
+ show C_10 = "C_10"
+ show C_11 = "C_11"
+ show C_1N = "C_1N"
+
{- Note [Demand notation]
~~~~~~~~~~~~~~~~~~~~~~~~~
This Note should be kept up to date with the documentation of `-fstrictness`
@@ -1810,10 +1957,11 @@ instance Outputable Card where
-- | See Note [Demand notation]
instance Outputable Demand where
- ppr dmd@(n :* sd)
- | isAbs n = ppr n -- If absent, sd is arbitrary
- | dmd == polyDmd n = ppr n -- Print UU as just U
- | otherwise = ppr n <> ppr sd
+ ppr AbsDmd = char 'A'
+ ppr BotDmd = char 'B'
+ ppr (C_0N :* Poly C_0N) = char 'L' -- Print LL as just L
+ ppr (C_1N :* Poly C_1N) = char 'S' -- Dito SS
+ ppr (n :* sd) = ppr n <> ppr sd
-- | See Note [Demand notation]
instance Outputable SubDemand where
@@ -1868,8 +2016,14 @@ instance Binary Card where
_ -> pprPanic "Binary:Card" (ppr (fromIntegral h :: Int))
instance Binary Demand where
- put_ bh (n :* sd) = put_ bh n *> put_ bh sd
- get bh = (:*) <$> get bh <*> get bh
+ put_ bh (n :* sd) = put_ bh n *> case n of
+ C_00 -> return ()
+ C_10 -> return ()
+ _ -> put_ bh sd
+ get bh = get bh >>= \n -> case n of
+ C_00 -> return AbsDmd
+ C_10 -> return BotDmd
+ _ -> (n :*) <$> get bh
instance Binary SubDemand where
put_ bh (Poly sd) = putByte bh 0 *> put_ bh sd