diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2013-01-24 14:50:50 +0000 |
---|---|---|
committer | Simon Peyton Jones <simonpj@microsoft.com> | 2013-01-24 14:50:50 +0000 |
commit | d3b8991be3875302ca6d1a4ef6e72891e9567dd5 (patch) | |
tree | afbf9416c2d569dba29fafdd7478aad02f4e0891 /compiler | |
parent | b4e86fa8b7a3c7527632aa8ba4b4a94a8719bfa5 (diff) | |
download | haskell-d3b8991be3875302ca6d1a4ef6e72891e9567dd5.tar.gz |
Introduce CPR for sum types (Trac #5075)
The main payload of this patch is to extend CPR so that it
detects when a function always returns a result constructed
with the *same* constructor, even if the constructor comes from
a sum type. This doesn't matter very often, but it does improve
some things (results below).
Binary sizes increase a little bit, I think because there are more
wrappers. This with -split-objs. Without split-ojbs binary sizes
increased by 6% even for HelloWorld.hs. It's hard to see exactly why,
but I think it was because System.Posix.Types.o got included in the
linked binary, whereas it didn't before.
Program Size Allocs Runtime Elapsed TotalMem
fluid +1.8% -0.3% 0.01 0.01 +0.0%
tak +2.2% -0.2% 0.02 0.02 +0.0%
ansi +1.7% -0.3% 0.00 0.00 +0.0%
cacheprof +1.6% -0.3% +0.6% +0.5% +1.4%
parstof +1.4% -4.4% 0.00 0.00 +0.0%
reptile +2.0% +0.3% 0.02 0.02 +0.0%
----------------------------------------------------------------------
Min +1.1% -4.4% -4.7% -4.7% -15.0%
Max +2.3% +0.3% +8.3% +9.4% +50.0%
Geometric Mean +1.9% -0.1% +0.6% +0.7% +0.3%
Other things in this commit
~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Got rid of the Lattice class in Demand
* Refactored the way that products and newtypes are
decomposed (no change in functionality)
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/basicTypes/BasicTypes.lhs | 17 | ||||
-rw-r--r-- | compiler/basicTypes/DataCon.lhs | 60 | ||||
-rw-r--r-- | compiler/basicTypes/Demand.lhs | 429 | ||||
-rw-r--r-- | compiler/basicTypes/MkId.lhs | 11 | ||||
-rw-r--r-- | compiler/cmm/CLabel.hs | 1 | ||||
-rw-r--r-- | compiler/coreSyn/CoreLint.lhs | 12 | ||||
-rw-r--r-- | compiler/coreSyn/CoreSyn.lhs | 7 | ||||
-rw-r--r-- | compiler/deSugar/DsCCall.lhs | 45 | ||||
-rw-r--r-- | compiler/deSugar/DsForeign.lhs | 2 | ||||
-rw-r--r-- | compiler/prelude/PrelRules.lhs | 2 | ||||
-rw-r--r-- | compiler/simplCore/Simplify.lhs | 5 | ||||
-rw-r--r-- | compiler/specialise/SpecConstr.lhs | 2 | ||||
-rw-r--r-- | compiler/stranal/DmdAnal.lhs | 49 | ||||
-rw-r--r-- | compiler/stranal/WwLib.lhs | 210 | ||||
-rw-r--r-- | compiler/types/Coercion.lhs | 62 | ||||
-rw-r--r-- | compiler/types/FamInstEnv.lhs | 26 | ||||
-rw-r--r-- | compiler/types/TyCon.lhs | 25 | ||||
-rw-r--r-- | compiler/types/Type.lhs | 26 | ||||
-rw-r--r-- | compiler/vectorise/Vectorise/Exp.hs | 10 |
19 files changed, 515 insertions, 486 deletions
diff --git a/compiler/basicTypes/BasicTypes.lhs b/compiler/basicTypes/BasicTypes.lhs index be6a78fdd1..a4fb5590a2 100644 --- a/compiler/basicTypes/BasicTypes.lhs +++ b/compiler/basicTypes/BasicTypes.lhs @@ -26,6 +26,8 @@ types that module BasicTypes( Version, bumpVersion, initialVersion, + ConTag, fIRST_TAG, + Arity, RepArity, Alignment, @@ -113,6 +115,21 @@ type RepArity = Int %************************************************************************ %* * + Constructor tags +%* * +%************************************************************************ + +\begin{code} +-- | Type of the tags associated with each constructor possibility +type ConTag = Int + +fIRST_TAG :: ConTag +-- ^ Tags are allocated from here for real constructors +fIRST_TAG = 1 +\end{code} + +%************************************************************************ +%* * \subsection[Alignment]{Alignment} %* * %************************************************************************ diff --git a/compiler/basicTypes/DataCon.lhs b/compiler/basicTypes/DataCon.lhs index 47e37a9c0e..88b09f3d07 100644 --- a/compiler/basicTypes/DataCon.lhs +++ b/compiler/basicTypes/DataCon.lhs @@ -41,9 +41,6 @@ module DataCon ( isVanillaDataCon, classDataCon, dataConCannotMatch, isBanged, isMarkedStrict, eqHsBang, - -- * Splitting product types - splitProductType_maybe, splitProductType, - -- ** Promotion related functions isPromotableTyCon, promoteTyCon, promoteDataCon, promoteDataCon_maybe @@ -461,13 +458,6 @@ data HsBang -- StrictnessMark is internal only, used to indicate strictness -- of the DataCon *worker* fields data StrictnessMark = MarkedStrict | NotMarkedStrict - --- | Type of the tags associated with each constructor possibility -type ConTag = Int - -fIRST_TAG :: ConTag --- ^ Tags are allocated from here for real constructors -fIRST_TAG = 1 \end{code} Note [Data con representation] @@ -994,56 +984,6 @@ dataConCannotMatch tys con \end{code} %************************************************************************ -%* * -\subsection{Splitting products} -%* * -%************************************************************************ - -\begin{code} --- | Extract the type constructor, type argument, data constructor and it's --- /representation/ argument types from a type if it is a product type. --- --- Precisely, we return @Just@ for any type that is all of: --- --- * Concrete (i.e. constructors visible) --- --- * Single-constructor --- --- * Not existentially quantified --- --- Whether the type is a @data@ type or a @newtype@ -splitProductType_maybe - :: Type -- ^ A product type, perhaps - -> Maybe (TyCon, -- The type constructor - [Type], -- Type args of the tycon - DataCon, -- The data constructor - [Type]) -- Its /representation/ arg types - - -- Rejecing existentials is conservative. Maybe some things - -- could be made to work with them, but I'm not going to sweat - -- it through till someone finds it's important. - -splitProductType_maybe ty - = case splitTyConApp_maybe ty of - Just (tycon,ty_args) - | isProductTyCon tycon -- Includes check for non-existential, - -- and for constructors visible - -> Just (tycon, ty_args, data_con, dataConInstArgTys data_con ty_args) - where - data_con = ASSERT( not (null (tyConDataCons tycon)) ) - head (tyConDataCons tycon) - _other -> Nothing - --- | As 'splitProductType_maybe', but panics if the 'Type' is not a product type -splitProductType :: String -> Type -> (TyCon, [Type], DataCon, [Type]) -splitProductType str ty - = case splitProductType_maybe ty of - Just stuff -> stuff - Nothing -> pprPanic (str ++ ": not a product") (pprType ty) -\end{code} - - -%************************************************************************ %* * Promoting of data types to the kind level %* * diff --git a/compiler/basicTypes/Demand.lhs b/compiler/basicTypes/Demand.lhs index ad778d16ad..364adadc6b 100644 --- a/compiler/basicTypes/Demand.lhs +++ b/compiler/basicTypes/Demand.lhs @@ -22,9 +22,10 @@ module Demand ( DmdResult, CPRResult, PureResult, isBotRes, isTopRes, resTypeArgDmd, - topRes, botRes, cprRes, - appIsBottom, isBottomingSig, pprIfaceStrictSig, returnsCPR, - StrictSig(..), mkStrictSig, topSig, botSig, cprSig, + topRes, botRes, cprProdRes, cprSumRes, + appIsBottom, isBottomingSig, pprIfaceStrictSig, + returnsCPR, returnsCPRProd, returnsCPR_maybe, + StrictSig(..), mkStrictSig, topSig, botSig, cprProdSig, isTopSig, splitStrictSig, increaseStrictSigArity, seqStrDmd, seqStrDmdList, seqAbsDmd, seqAbsDmdList, @@ -48,44 +49,24 @@ import UniqFM import Util import BasicTypes import Binary -import Maybes ( expectJust ) +import Maybes ( isJust, expectJust ) \end{code} %************************************************************************ %* * -\subsection{Lattice-like structure for domains} -%* * -%************************************************************************ - -\begin{code} - -class LatticeLike a where - bot :: a - top :: a - pre :: a -> a -> Bool - lub :: a -> a -> a - both :: a -> a -> a - --- False < True -instance LatticeLike Bool where - bot = False - top = True --- x `pre` y <==> (x => y) - pre x y = (not x) || y - lub = (||) - both = (&&) - -\end{code} - - -%************************************************************************ -%* * \subsection{Strictness domain} %* * %************************************************************************ -\begin{code} + Lazy + | + Str + / \ + SCall SProd + \ / + HyperStr +\begin{code} -- Vanilla strictness domain data StrDmd = HyperStr -- Hyper-strict @@ -132,42 +113,43 @@ instance Outputable StrDmd where ppr Str = char 'S' ppr (SProd sx) = char 'S' <> parens (hcat (map ppr sx)) --- LatticeLike implementation for strictness demands -instance LatticeLike StrDmd where - bot = HyperStr - top = Lazy - - pre _ Lazy = True - pre HyperStr _ = True - pre (SCall s1) (SCall s2) = pre s1 s2 - pre (SCall _) Str = True - pre (SProd _) Str = True - pre (SProd sx1) (SProd sx2) - | length sx1 == length sx2 = all (== True) $ zipWith pre sx1 sx2 - pre x y = x == y - - lub x y | x == y = x - lub y x | x `pre` y = lub x y - lub HyperStr s = s - lub _ Lazy = strTop - lub (SProd _) Str = strStr - lub (SProd sx1) (SProd sx2) - | length sx1 == length sx2 = strProd $ zipWith lub sx1 sx2 - | otherwise = strStr - lub (SCall s1) (SCall s2) = strCall (s1 `lub` s2) - lub (SCall _) Str = strStr - lub _ _ = strTop - - both x y | x == y = x - both y x | x `pre` y = both x y - both HyperStr _ = strBot - both s Lazy = s - both s@(SProd _) Str = s - both (SProd sx1) (SProd sx2) - | length sx1 == length sx2 = strProd $ zipWith both sx1 sx2 - both (SCall s1) (SCall s2) = strCall (s1 `both` s2) - both s@(SCall _) Str = s - both _ _ = strBot +lubStr :: StrDmd -> StrDmd -> StrDmd +lubStr HyperStr s = s +lubStr (SCall s1) HyperStr = SCall s1 +lubStr (SCall _) Lazy = Lazy +lubStr (SCall _) Str = Str +lubStr (SCall s1) (SCall s2) = SCall (s1 `lubStr` s2) +lubStr (SCall _) (SProd _) = Str +lubStr (SProd _) HyperStr = HyperStr +lubStr (SProd _) Lazy = Lazy +lubStr (SProd _) Str = Str +lubStr (SProd s1) (SProd s2) + | length s1 == length s2 = SProd (zipWith lubStr s1 s2) + | otherwise = Str +lubStr (SProd _) (SCall _) = Str +lubStr Str Lazy = Lazy +lubStr Str _ = Str +lubStr Lazy _ = Lazy + +bothStr :: StrDmd -> StrDmd -> StrDmd +bothStr HyperStr _ = HyperStr +bothStr Lazy s = s +bothStr Str Lazy = Str +bothStr Str s = s +bothStr (SCall _) HyperStr = HyperStr +bothStr (SCall s1) Lazy = SCall s1 +bothStr (SCall s1) Str = SCall s1 +bothStr (SCall s1) (SCall s2) = SCall (s1 `bothStr` s2) +bothStr (SCall _) (SProd _) = HyperStr -- Weird + +bothStr (SProd _) HyperStr = HyperStr +bothStr (SProd s1) Lazy = SProd s1 +bothStr (SProd s1) Str = SProd s1 +bothStr (SProd s1) (SProd s2) + | length s1 == length s2 = SProd (zipWith bothStr s1 s2) + | otherwise = HyperStr -- Weird +bothStr (SProd _) (SCall _) = HyperStr + -- utility functions to deal with memory leaks seqStrDmd :: StrDmd -> () @@ -179,6 +161,10 @@ seqStrDmdList :: [StrDmd] -> () seqStrDmdList [] = () seqStrDmdList (d:ds) = seqStrDmd d `seq` seqStrDmdList ds +isStrict :: StrDmd -> Bool +isStrict Lazy = False +isStrict _ = True + -- Splitting polymorphic demands splitStrProdDmd :: Int -> StrDmd -> [StrDmd] splitStrProdDmd n Lazy = replicate n Lazy @@ -196,7 +182,7 @@ splitStrProdDmd n (SCall d) = ASSERT( n == 1 ) [d] Note [Don't optimise UProd(Used) to Used] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -An AbsDmds +These two AbsDmds: UProd [Used, Used] and Used are semantically equivalent, but we do not turn the former into the latter, for a regrettable-subtle reason. Suppose we did. @@ -214,14 +200,29 @@ This too would get <Str, Used>, but this time there really isn't any point in w/w since the components of the pair are not used at all. So the solution is: don't collapse UProd [Used,Used] to Used; intead -leave it as-is. - +leave it as-is. In effect we are using the AbsDmd to do a little bit +of boxity analysis. Not very nice. + + + Used + / \ + UCall UProd + \ / + UHead + | + Abs \begin{code} data AbsDmd = Abs -- Definitely unused -- Bottom of the lattice + | UHead -- May be used; but its sub-components are + -- definitely *not* used. Roughly U(AAA) + -- Eg the usage of x in x `seq` e + -- A polymorphic demand: used for values of all types, + -- including a type variable + | UCall AbsDmd -- Call demand for absence -- Used only for values of function type @@ -231,12 +232,6 @@ data AbsDmd -- [Invariant] Not all components are Abs -- (in that case, use UHead) - | UHead -- May be used; but its sub-components are - -- definitely *not* used. - -- Eg the usage of x in x `seq` e - -- A polymorphic demand: used for values of all types, - -- including a type variable - | Used -- May be used; and its sub-components may be used -- Top of the lattice deriving ( Eq, Show ) @@ -267,32 +262,26 @@ absProd ux | all (== Abs) ux = UHead | otherwise = UProd ux -instance LatticeLike AbsDmd where - bot = absBot - top = absTop - - pre Abs _ = True - pre _ Used = True - pre UHead (UProd _) = True - pre (UCall u1) (UCall u2) = pre u1 u2 - pre (UProd ux1) (UProd ux2) - | length ux1 == length ux2 = all (== True) $ zipWith pre ux1 ux2 - pre x y = x == y - - lub x y | x == y = x - lub y x | x `pre` y = lub x y - lub Abs a = a - lub a Abs = a - lub UHead u = u - lub u UHead = u - lub (UProd ux1) (UProd ux2) - | length ux1 == length ux2 = absProd $ zipWith lub ux1 ux2 - lub (UCall u1) (UCall u2) = absCall (u1 `lub` u2) - lub (UProd ds) Used = UProd (map (`lub` Used) ds) - lub Used (UProd ds) = UProd (map (`lub` Used) ds) - lub _ _ = Used - - both = lub +lubAbs :: AbsDmd -> AbsDmd -> AbsDmd +lubAbs Abs x = x +lubAbs UHead Abs = UHead +lubAbs UHead x = x +lubAbs (UCall u1) Abs = UCall u1 +lubAbs (UCall u1) UHead = UCall u1 +lubAbs (UCall u1) (UCall u2) = UCall (u1 `lubAbs` u2) +lubAbs (UCall _) _ = Used +lubAbs (UProd u1) Abs = UProd u1 +lubAbs (UProd u1) UHead = UProd u1 +lubAbs (UProd u1) (UProd u2) + | length u1 == length u2 = UProd (zipWith lubAbs u1 u2) + | otherwise = Used +lubAbs (UProd _) (UCall _) = Used +lubAbs (UProd ds) Used = UProd (map (`lubAbs` Used) ds) -- Note [Don't optimise UProd(Used) to Used] +lubAbs Used (UProd ds) = UProd (map (`lubAbs` Used) ds) -- Note [Don't optimise UProd(Used) to Used] +lubAbs Used _ = Used + +bothAbs :: AbsDmd -> AbsDmd -> AbsDmd +bothAbs = lubAbs -- utility functions seqAbsDmd :: AbsDmd -> () @@ -345,33 +334,22 @@ mkProdDmd dx sp = strProd $ map strd dx up = absProd $ map absd dx -instance LatticeLike JointDmd where - bot = botDmd - top = topDmd - pre = preDmd - lub = lubDmd - both = bothDmd - absDmd :: JointDmd -absDmd = mkJointDmd top bot +absDmd = mkJointDmd strTop absBot topDmd :: JointDmd -topDmd = mkJointDmd top top +topDmd = mkJointDmd strTop absTop botDmd :: JointDmd -botDmd = mkJointDmd bot bot - -preDmd :: JointDmd -> JointDmd -> Bool -preDmd (JD {strd = s1, absd = a1}) - (JD {strd = s2, absd = a2}) = pre s1 s2 && pre a1 a2 +botDmd = mkJointDmd strBot absBot lubDmd :: JointDmd -> JointDmd -> JointDmd lubDmd (JD {strd = s1, absd = a1}) - (JD {strd = s2, absd = a2}) = mkJointDmd (lub s1 s2) (lub a1 a2) + (JD {strd = s2, absd = a2}) = mkJointDmd (lubStr s1 s2) (lubAbs a1 a2) bothDmd :: JointDmd -> JointDmd -> JointDmd bothDmd (JD {strd = s1, absd = a1}) - (JD {strd = s2, absd = a2}) = mkJointDmd (both s1 s2) (both a1 a2) + (JD {strd = s2, absd = a2}) = mkJointDmd (bothStr s1 s2) (bothAbs a1 a2) isTopDmd :: JointDmd -> Bool isTopDmd (JD {strd = Lazy, absd = Used}) = True @@ -398,13 +376,13 @@ seqDemandList [] = () seqDemandList (d:ds) = seqDemand d `seq` seqDemandList ds isStrictDmd :: Demand -> Bool -isStrictDmd (JD {strd = x}) = x /= top +isStrictDmd (JD {strd = x}) = isStrict x isUsedDmd :: Demand -> Bool -isUsedDmd (JD {absd = x}) = x /= bot +isUsedDmd (JD {absd = x}) = isUsed x isUsed :: AbsDmd -> Bool -isUsed x = x /= bot +isUsed x = x /= absBot someCompUsed :: AbsDmd -> Bool someCompUsed Used = True @@ -416,7 +394,7 @@ evalDmd :: JointDmd evalDmd = mkJointDmd strStr absTop defer :: Demand -> Demand -defer (JD {absd = a}) = mkJointDmd top a +defer (JD {absd = a}) = mkJointDmd strTop a -- use :: Demand -> Demand -- use (JD {strd = d}) = mkJointDmd d top @@ -424,7 +402,6 @@ defer (JD {absd = a}) = mkJointDmd top a Note [Dealing with call demands] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Call demands are constructed and deconstructed coherently for strictness and absence. For instance, the strictness signature for the following function @@ -436,8 +413,7 @@ should be: <L,C(U(AU))>m \begin{code} mkCallDmd :: JointDmd -> JointDmd -mkCallDmd (JD {strd = d, absd = a}) - = mkJointDmd (strCall d) (absCall a) +mkCallDmd (JD {strd = d, absd = a}) = mkJointDmd (strCall d) (absCall a) peelCallDmd :: JointDmd -> Maybe JointDmd -- Exploiting the fact that @@ -537,32 +513,39 @@ data PureResult = TopRes -- Nothing known, assumed to be just lazy | BotRes -- Diverges or errors deriving( Eq, Show ) -instance LatticeLike PureResult where - bot = BotRes - top = TopRes - pre x y = (x == y) || (y == top) - lub x y | x == y = x - lub _ _ = top - both x y | x == y = x - both _ _ = bot +lubPR :: PureResult -> PureResult -> PureResult +lubPR BotRes pr = pr +lubPR TopRes _ = TopRes + +bothPR :: PureResult -> PureResult -> PureResult +bothPR BotRes _ = BotRes +bothPR TopRes pr = pr ------------------------------------------------------------------------ -- Constructed Product Result ------------------------------------------------------------------------ -data CPRResult = NoCPR - | RetCPR +data CPRResult = NoCPR -- Top of the lattice + | RetProd -- Returns a constructor from a product type + | RetSum ConTag -- Returns a constructor from a sum type with this tag + | BotCPR -- Returns a constructor with any tag + -- Bottom of the domain deriving( Eq, Show ) -instance LatticeLike CPRResult where - bot = RetCPR - top = NoCPR - pre x y = (x == y) || (y == top) - lub x y | x == y = x - lub _ _ = top - both x y | x == y = x - both _ _ = bot +lubCPR :: CPRResult -> CPRResult -> CPRResult +lubCPR BotCPR r = r +lubCPR RetProd BotCPR = RetProd +lubCPR (RetSum t) BotCPR = RetSum t +lubCPR (RetSum t1) (RetSum t2) + | t1 == t2 = RetSum t1 +lubCPR RetProd RetProd = RetProd +lubCPR _ _ = NoCPR + +bothCPR :: CPRResult -> CPRResult -> CPRResult +-- See Note [Asymmetry of 'both' for DmdType and DmdResult] +bothCPR r _ = r + ------------------------------------------------------------------------ -- Combined demand result -- @@ -571,46 +554,39 @@ instance LatticeLike CPRResult where data DmdResult = DR { res :: PureResult, cpr :: CPRResult } deriving ( Eq ) --- TODO rework DmdResult to make it more clear -instance LatticeLike DmdResult where - bot = botRes - top = topRes - - pre x _ | x == bot = True - pre _ x | x == top = True - pre (DR s1 a1) (DR s2 a2) = (pre s1 s2) && (pre a1 a2) - - lub r r' | isBotRes r = r' - lub r r' | isBotRes r' = r - lub r r' - | returnsCPR r && returnsCPR r' = r - lub _ _ = top +lubDmdResult :: DmdResult -> DmdResult -> DmdResult +lubDmdResult (DR pr1 cpr1) (DR pr2 cpr2) = DR (pr1 `lubPR` pr2) (cpr1 `lubCPR` cpr2) - both _ r2 | isBotRes r2 = r2 - both r1 _ = r1 +bothDmdResult :: DmdResult -> DmdResult -> DmdResult +bothDmdResult (DR pr1 cpr1) (DR pr2 cpr2) = DR (pr1 `bothPR` pr2) (cpr1 `bothCPR` cpr2) -- Pretty-printing instance Outputable DmdResult where - ppr (DR {res=TopRes, cpr=RetCPR}) = char 'm' -- DDDr without ambiguity - ppr (DR {res=BotRes}) = char 'b' + ppr (DR {res=TopRes, cpr=RetProd}) = char 'm' + ppr (DR {res=TopRes, cpr=RetSum n}) = char 'm' <> int n + ppr (DR {res=BotRes}) = char 'b' ppr _ = empty -- Keep these distinct from Demand letters mkDmdResult :: PureResult -> CPRResult -> DmdResult -mkDmdResult BotRes RetCPR = botRes +-- mkDmdResult BotRes (RetCPR _) = botRes -- SLPJ: commenting out; unnecessary? mkDmdResult x y = DR {res=x, cpr=y} seqDmdResult :: DmdResult -> () seqDmdResult (DR {res=x, cpr=y}) = x `seq` y `seq` () -- [cprRes] lets us switch off CPR analysis --- by making sure that everything uses TopRes instead of RetCPR --- Assuming, of course, that they don't mention RetCPR by name. --- They should onlyu use retCPR -topRes, botRes, cprRes :: DmdResult +-- by making sure that everything uses TopRes +topRes, botRes :: DmdResult topRes = mkDmdResult TopRes NoCPR -botRes = mkDmdResult BotRes NoCPR -cprRes | opt_CprOff = topRes - | otherwise = mkDmdResult TopRes RetCPR +botRes = mkDmdResult BotRes BotCPR + +cprSumRes :: ConTag -> DmdResult +cprSumRes tag | opt_CprOff = topRes + | otherwise = mkDmdResult TopRes (RetSum tag) +cprProdRes :: DmdResult +cprProdRes | opt_CprOff = topRes + | otherwise = mkDmdResult TopRes RetProd + isTopRes :: DmdResult -> Bool isTopRes (DR {res=TopRes, cpr=NoCPR}) = True @@ -621,16 +597,24 @@ isBotRes (DR {res=BotRes}) = True isBotRes _ = False returnsCPR :: DmdResult -> Bool -returnsCPR (DR {res=TopRes, cpr=RetCPR}) = True -returnsCPR _ = False +returnsCPR dr = isJust (returnsCPR_maybe dr) + +returnsCPRProd :: DmdResult -> Bool +returnsCPRProd (DR {res=TopRes, cpr=RetProd}) = True +returnsCPRProd _ = False + +returnsCPR_maybe :: DmdResult -> Maybe ConTag +returnsCPR_maybe (DR {res=TopRes, cpr=RetSum t}) = Just t +returnsCPR_maybe (DR {res=TopRes, cpr=RetProd}) = Just fIRST_TAG +returnsCPR_maybe _ = Nothing resTypeArgDmd :: DmdResult -> Demand -- TopRes and BotRes are polymorphic, so that -- BotRes === Bot -> BotRes === ... -- TopRes === Top -> TopRes === ... -- This function makes that concrete -resTypeArgDmd r | isBotRes r = bot -resTypeArgDmd _ = top +resTypeArgDmd r | isBotRes r = botDmd +resTypeArgDmd _ = topDmd \end{code} %************************************************************************ @@ -647,10 +631,12 @@ worthSplittingFun ds res -- worthSplitting returns False for an empty list of demands, -- and hence do_strict_ww is False if arity is zero and there is no CPR where + worth_it (JD {absd=Abs}) = True -- Absent arg + -- See Note [Worker-wrapper for bottoming functions] - worth_it (JD {strd=HyperStr, absd=a}) = isUsed a -- A Hyper-strict argument, safe to do W/W + worth_it (JD {strd=HyperStr, absd=UProd _}) = True + -- See Note [Worthy functions for Worker-Wrapper split] - worth_it (JD {absd=Abs}) = True -- Absent arg worth_it (JD {strd=SProd _}) = True -- Product arg to evaluate worth_it (JD {strd=Str, absd=UProd _}) = True -- Strictly used product arg worth_it (JD {strd=Str, absd=UHead}) = True @@ -731,6 +717,19 @@ The re-boxing code won't go away unless error_fn gets a wrapper too. [We don't do reboxing now, but in general it's better to pass an unboxed thing to f, and have it reboxed in the error cases....] +However we *don't* want to do this when the argument is not actually +taken apart in the function at all. Otherwise we risk decomposing a +masssive tuple which is barely used. Example: + + f :: ((Int,Int) -> String) -> (Int,Int) -> a + f g pr = error (g pr) + + main = print (f fst (1, error "no")) + +Here, f does not take 'pr' apart, and it's stupid to do so. +Imagine that it had millions of fields. This actually happened +in GHC itself where the tuple was DynFlags + %************************************************************************ %* * @@ -781,7 +780,14 @@ Note [Asymmetry of 'both' for DmdType and DmdResult] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'both' for DmdTypes is *assymetrical*, because there is only one result! For example, given (e1 e2), we get a DmdType dt1 for e1, use -its arg demand to analyse e2 giving dt2, and then do (dt1 `both` dt2). +its arg demand to analyse e2 giving dt2, and then do (dt1 `bothType` dt2). +Similarly with + case e of { p -> rhs } +we get dt_scrut from the scrutinee and dt_rhs from the RHS, and then +compute (dt_rhs `bothType` dt_scrut). + +We take the CPR info from FIRST argument, but combine both to get +termination info. \begin{code} @@ -791,25 +797,12 @@ instance Eq DmdType where (DmdType fv2 ds2 res2) = ufmToList fv1 == ufmToList fv2 && ds1 == ds2 && res1 == res2 -instance LatticeLike DmdType where - bot = botDmdType - top = topDmdType - pre = preDmdType - lub = lubDmdType - both = bothDmdType - -preDmdType :: DmdType -> DmdType -> Bool -preDmdType (DmdType _ ds1 res1) (DmdType _ ds2 res2) - = (res1 `pre` res2) - && (length ds1 == length ds2) - && all (\(x, y) -> x `pre` y) (zip ds1 ds2) - lubDmdType :: DmdType -> DmdType -> DmdType lubDmdType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2) - = DmdType lub_fv2 (lub_ds ds1 ds2) (r1 `lub` r2) + = DmdType lub_fv2 (lub_ds ds1 ds2) (r1 `lubDmdResult` r2) where - absLub = lub absDmd - lub_fv = plusVarEnv_C lub fv1 fv2 + absLub = lubDmd absDmd + lub_fv = plusVarEnv_C lubDmd fv1 fv2 -- Consider (if x then y else []) with demand V -- Then the first branch gives {y->V} and the second -- *implicitly* has {y->A}. So we must put {y->(V `lub` A)} @@ -819,10 +812,10 @@ lubDmdType (DmdType fv1 ds1 r1) (DmdType fv2 ds2 r2) -- lub is the identity for Bot -- Extend the shorter argument list to match the longer - lub_ds (d1:ds1) (d2:ds2) = lub d1 d2 : lub_ds ds1 ds2 + lub_ds (d1:ds1) (d2:ds2) = lubDmd d1 d2 : lub_ds ds1 ds2 lub_ds [] [] = [] - lub_ds ds1 [] = map (`lub` resTypeArgDmd r2) ds1 - lub_ds [] ds2 = map (resTypeArgDmd r1 `lub`) ds2 + lub_ds ds1 [] = map (`lubDmd` resTypeArgDmd r2) ds1 + lub_ds [] ds2 = map (resTypeArgDmd r1 `lubDmd`) ds2 bothDmdType :: DmdType -> DmdType -> DmdType bothDmdType (DmdType fv1 ds1 r1) (DmdType fv2 _ r2) @@ -831,11 +824,11 @@ bothDmdType (DmdType fv1 ds1 r1) (DmdType fv2 _ r2) -- using its second arg just for its free-var info. -- NB: Don't forget about r2! It might be BotRes, which is -- a bottom demand on all the in-scope variables. - = DmdType both_fv2 ds1 (r1 `both` r2) + = DmdType both_fv2 ds1 (r1 `bothDmdResult` r2) where - both_fv = plusVarEnv_C both fv1 fv2 - both_fv1 = modifyEnv (isBotRes r1) (`both` bot) fv2 fv1 both_fv - both_fv2 = modifyEnv (isBotRes r2) (`both` bot) fv1 fv2 both_fv1 + both_fv = plusVarEnv_C bothDmd fv1 fv2 + both_fv1 = modifyEnv (isBotRes r1) (`bothDmd` botDmd) fv2 fv1 both_fv + both_fv2 = modifyEnv (isBotRes r2) (`bothDmd` botDmd) fv1 fv2 both_fv1 instance Outputable DmdType where @@ -851,10 +844,12 @@ instance Outputable DmdType where emptyDmdEnv :: VarEnv Demand emptyDmdEnv = emptyVarEnv -topDmdType, botDmdType, cprDmdType :: DmdType +topDmdType, botDmdType :: DmdType topDmdType = DmdType emptyDmdEnv [] topRes botDmdType = DmdType emptyDmdEnv [] botRes -cprDmdType = DmdType emptyDmdEnv [] cprRes + +cprProdDmdType :: DmdType +cprProdDmdType = DmdType emptyDmdEnv [] cprProdRes isTopDmdType :: DmdType -> Bool isTopDmdType (DmdType env [] res) @@ -882,7 +877,7 @@ splitDmdTy (DmdType fv (dmd:dmds) res_ty) = (dmd, DmdType fv dmds res_ty) splitDmdTy ty@(DmdType _ [] res_ty) = (resTypeArgDmd res_ty, ty) deferType :: DmdType -> DmdType -deferType (DmdType fv _ _) = DmdType (deferEnv fv) [] top +deferType (DmdType fv _ _) = DmdType (deferEnv fv) [] topRes deferEnv :: DmdEnv -> DmdEnv deferEnv fv = mapVarEnv defer fv @@ -956,7 +951,7 @@ splitStrictSig (StrictSig (DmdType _ dmds res)) = (dmds, res) increaseStrictSigArity :: Int -> StrictSig -> StrictSig -- Add extra arguments to a strictness signature increaseStrictSigArity arity_increase (StrictSig (DmdType env dmds res)) - = StrictSig (DmdType env (replicate arity_increase top ++ dmds) res) + = StrictSig (DmdType env (replicate arity_increase topDmd ++ dmds) res) isTopSig :: StrictSig -> Bool isTopSig (StrictSig ty) = isTopDmdType ty @@ -964,10 +959,12 @@ isTopSig (StrictSig ty) = isTopDmdType ty isBottomingSig :: StrictSig -> Bool isBottomingSig (StrictSig (DmdType _ _ res)) = isBotRes res -topSig, botSig, cprSig:: StrictSig +topSig, botSig :: StrictSig topSig = StrictSig topDmdType botSig = StrictSig botDmdType -cprSig = StrictSig cprDmdType + +cprProdSig :: StrictSig +cprProdSig = StrictSig cprProdDmdType dmdTransformSig :: StrictSig -> Demand -> DmdType -- (dmdTransformSig fun_sig dmd) considers a call to a function whose @@ -977,8 +974,8 @@ dmdTransformSig (StrictSig dmd_ty@(DmdType _ arg_ds _)) dmd = go arg_ds dmd where go [] dmd - | isBotDmd dmd = bot -- Transform bottom demand to bottom type - | otherwise = dmd_ty -- Saturated + | isBotDmd dmd = botDmdType -- Transform bottom demand to bottom type + | otherwise = dmd_ty -- Saturated go (_:as) dmd = case peelCallDmd dmd of Just dmd' -> go as dmd' Nothing -> deferType dmd_ty @@ -1096,8 +1093,8 @@ instance Binary PureResult where get bh = do h <- getByte bh case h of - 0 -> return bot - _ -> return top + 0 -> return BotRes + _ -> return TopRes instance Binary StrictSig where put_ bh (StrictSig aa) = do @@ -1117,14 +1114,18 @@ instance Binary DmdType where return (DmdType emptyDmdEnv ds dr) instance Binary CPRResult where - put_ bh RetCPR = do putByte bh 0 - put_ bh NoCPR = do putByte bh 1 + put_ bh (RetSum n) = do { putByte bh 0; put_ bh n } + put_ bh RetProd = putByte bh 1 + put_ bh NoCPR = putByte bh 2 + put_ bh BotCPR = putByte bh 3 get bh = do h <- getByte bh case h of - 0 -> return bot - _ -> return top + 0 -> do { n <- get bh; return (RetSum n) } + 1 -> return RetProd + 2 -> return NoCPR + _ -> return BotCPR instance Binary DmdResult where put_ bh (DR {res=x, cpr=y}) = do put_ bh x; put_ bh y diff --git a/compiler/basicTypes/MkId.lhs b/compiler/basicTypes/MkId.lhs index 3fdf86dd02..8957924618 100644 --- a/compiler/basicTypes/MkId.lhs +++ b/compiler/basicTypes/MkId.lhs @@ -425,16 +425,17 @@ mkDataConWorkId wkr_name data_con dataConCPR :: DataCon -> DmdResult dataConCPR con - | isProductTyCon tycon - , isDataTyCon tycon + | isDataTyCon tycon -- Real data types only; that is, + -- not unboxed tuples or newtypes + , isVanillaDataCon con -- No existentials , wkr_arity > 0 , wkr_arity <= mAX_CPR_SIZE - = cprRes + = if is_prod then cprProdRes + else cprSumRes (dataConTag con) | otherwise = topRes - -- RetCPR is only true for products that are real data types; - -- that is, not unboxed tuples or [non-recursive] newtypes where + is_prod = isProductTyCon tycon tycon = dataConTyCon con wkr_arity = dataConRepArity con diff --git a/compiler/cmm/CLabel.hs b/compiler/cmm/CLabel.hs index 259f31a1b8..ebc9e53c72 100644 --- a/compiler/cmm/CLabel.hs +++ b/compiler/cmm/CLabel.hs @@ -107,7 +107,6 @@ module CLabel ( import IdInfo import BasicTypes import Packages -import DataCon import Module import Name import Unique diff --git a/compiler/coreSyn/CoreLint.lhs b/compiler/coreSyn/CoreLint.lhs index ac3be95983..502de84a81 100644 --- a/compiler/coreSyn/CoreLint.lhs +++ b/compiler/coreSyn/CoreLint.lhs @@ -1303,12 +1303,12 @@ mkKindErrMsg tyvar arg_ty mkArityMsg :: Id -> MsgDoc mkArityMsg binder - = vcat [hsep [ptext (sLit "Demand type has "), - ppr (dmdTypeDepth dmd_ty), - ptext (sLit " arguments, rhs has "), - ppr (idArity binder), - ptext (sLit "arguments, "), - ppr binder], + = vcat [hsep [ptext (sLit "Demand type has"), + ppr (dmdTypeDepth dmd_ty), + ptext (sLit "arguments, rhs has"), + ppr (idArity binder), + ptext (sLit "arguments,"), + ppr binder], hsep [ptext (sLit "Binder's strictness signature:"), ppr dmd_ty] ] diff --git a/compiler/coreSyn/CoreSyn.lhs b/compiler/coreSyn/CoreSyn.lhs index 685975f982..9f34e4ac2e 100644 --- a/compiler/coreSyn/CoreSyn.lhs +++ b/compiler/coreSyn/CoreSyn.lhs @@ -31,7 +31,7 @@ module CoreSyn ( mkFloatLit, mkFloatLitFloat, mkDoubleLit, mkDoubleLitDouble, - mkConApp, mkTyBind, mkCoBind, + mkConApp, mkConApp2, mkTyBind, mkCoBind, varToCoreExpr, varsToCoreExprs, isId, cmpAltCon, cmpAlt, ltAlt, @@ -1133,6 +1133,11 @@ mkCoApps f args = foldl (\ e a -> App e (Coercion a)) f args mkVarApps f vars = foldl (\ e a -> App e (varToCoreExpr a)) f vars mkConApp con args = mkApps (Var (dataConWorkId con)) args +mkConApp2 :: DataCon -> [Type] -> [Var] -> Expr b +mkConApp2 con tys arg_ids = Var (dataConWorkId con) + `mkApps` map Type tys + `mkApps` map varToCoreExpr arg_ids + -- | Create a machine integer literal expression of type @Int#@ from an @Integer@. -- If you want an expression of type @Int@ use 'MkCore.mkIntExpr' diff --git a/compiler/deSugar/DsCCall.lhs b/compiler/deSugar/DsCCall.lhs index b5e38c8af2..c0f5019457 100644 --- a/compiler/deSugar/DsCCall.lhs +++ b/compiler/deSugar/DsCCall.lhs @@ -19,6 +19,7 @@ module DsCCall , unboxArg , boxResult , resultWrapper + , splitDataProductType_maybe ) where #include "HsVersions.h" @@ -191,7 +192,7 @@ unboxArg arg pprPanic "unboxArg: " (ppr l <+> ppr arg_ty) where arg_ty = exprType arg - maybe_product_type = splitProductType_maybe arg_ty + maybe_product_type = splitDataProductType_maybe arg_ty is_product_type = maybeToBool maybe_product_type Just (_, _, data_con, data_con_arg_tys) = maybe_product_type data_con_arity = dataConSourceArity data_con @@ -357,7 +358,7 @@ resultWrapper result_ty -- Data types with a single constructor, which has a single arg -- This includes types like Ptr and ForeignPtr - | Just (tycon, tycon_arg_tys, data_con, data_con_arg_tys) <- splitProductType_maybe result_ty, + | Just (tycon, tycon_arg_tys, data_con, data_con_arg_tys) <- splitDataProductType_maybe result_ty, dataConSourceArity data_con == 1 = do dflags <- getDynFlags let @@ -391,3 +392,43 @@ maybeNarrow dflags tycon && wORD_SIZE dflags > 4 = \e -> App (Var (mkPrimOpId Narrow32WordOp)) e | otherwise = id \end{code} + +%************************************************************************ +%* * +\subsection{Splitting products} +%* * +%************************************************************************ + +\begin{code} +-- | Extract the type constructor, type argument, data constructor and it's +-- /representation/ argument types from a type if it is a product type. +-- +-- Precisely, we return @Just@ for any type that is all of: +-- +-- * Concrete (i.e. constructors visible) +-- +-- * Single-constructor +-- +-- * Not existentially quantified +-- +-- Whether the type is a @data@ type or a @newtype@ +splitDataProductType_maybe + :: Type -- ^ A product type, perhaps + -> Maybe (TyCon, -- The type constructor + [Type], -- Type args of the tycon + DataCon, -- The data constructor + [Type]) -- Its /representation/ arg types + + -- Rejecing existentials is conservative. Maybe some things + -- could be made to work with them, but I'm not going to sweat + -- it through till someone finds it's important. + +splitDataProductType_maybe ty + | Just (tycon, ty_args) <- splitTyConApp_maybe ty + , Just con <- isDataProductTyCon_maybe tycon + = Just (tycon, ty_args, con, dataConInstArgTys con ty_args) + | otherwise + = Nothing +\end{code} + + diff --git a/compiler/deSugar/DsForeign.lhs b/compiler/deSugar/DsForeign.lhs index bf06be109f..9be8e96615 100644 --- a/compiler/deSugar/DsForeign.lhs +++ b/compiler/deSugar/DsForeign.lhs @@ -766,7 +766,7 @@ getPrimTyOf ty -- Except for Bool, the types we are interested in have a single constructor -- with a single primitive-typed argument (see TcType.legalFEArgTyCon). | otherwise = - case splitProductType_maybe rep_ty of + case splitDataProductType_maybe rep_ty of Just (_, _, data_con, [prim_ty]) -> ASSERT(dataConSourceArity data_con == 1) ASSERT2(isUnLiftedType prim_ty, ppr prim_ty) diff --git a/compiler/prelude/PrelRules.lhs b/compiler/prelude/PrelRules.lhs index b21d546ef7..2e55e497d7 100644 --- a/compiler/prelude/PrelRules.lhs +++ b/compiler/prelude/PrelRules.lhs @@ -31,7 +31,7 @@ import PrimOp ( PrimOp(..), tagToEnumKey ) import TysWiredIn import TysPrim import TyCon ( tyConDataCons_maybe, isEnumerationTyCon, isNewTyCon ) -import DataCon ( dataConTag, dataConTyCon, dataConWorkId, fIRST_TAG ) +import DataCon ( dataConTag, dataConTyCon, dataConWorkId ) import CoreUtils ( cheapEqExpr, exprIsHNF ) import CoreUnfold ( exprIsConApp_maybe ) import Type diff --git a/compiler/simplCore/Simplify.lhs b/compiler/simplCore/Simplify.lhs index f2ab037207..b736a1c27f 100644 --- a/compiler/simplCore/Simplify.lhs +++ b/compiler/simplCore/Simplify.lhs @@ -2037,7 +2037,7 @@ simplAlt env scrut' _ case_bndr' cont' (DataAlt con, vs, rhs) -- Bind the case-binder to (con args) ; let inst_tys' = tyConAppArgs (idType case_bndr') con_app :: OutExpr - con_app = mkConApp con (map Type inst_tys' ++ varsToCoreExprs vs') + con_app = mkConApp2 con inst_tys' vs' ; env'' <- addAltUnfoldings env' scrut' case_bndr' con_app ; rhs' <- simplExprC env'' rhs cont' @@ -2384,8 +2384,7 @@ mkDupableAlt env case_bndr (con, bndrs', rhs') = do where -- See Note [Case binders and join points] unf = mkInlineUnfolding Nothing rhs - rhs = mkConApp dc (map Type (tyConAppArgs scrut_ty) - ++ varsToCoreExprs bndrs') + rhs = mkConApp2 dc (tyConAppArgs scrut_ty) bndrs' LitAlt {} -> WARN( True, ptext (sLit "mkDupableAlt") <+> ppr case_bndr <+> ppr con ) diff --git a/compiler/specialise/SpecConstr.lhs b/compiler/specialise/SpecConstr.lhs index 3dc527475f..afc70f9b9e 100644 --- a/compiler/specialise/SpecConstr.lhs +++ b/compiler/specialise/SpecConstr.lhs @@ -1435,7 +1435,7 @@ calcSpecStrictness fn qvars pats go_one :: DmdEnv -> Demand -> CoreExpr -> DmdEnv go_one env d (Var v) = extendVarEnv_C bothDmd env v d go_one env d e - | Just ds <- splitProdDmd_maybe d + | Just ds <- splitProdDmd_maybe d -- NB: d does not have to be strict , (Var _, args) <- collectArgs e = go env ds args go_one env _ _ = env \end{code} diff --git a/compiler/stranal/DmdAnal.lhs b/compiler/stranal/DmdAnal.lhs index 9e38bb7c0d..0eca72fa00 100644 --- a/compiler/stranal/DmdAnal.lhs +++ b/compiler/stranal/DmdAnal.lhs @@ -221,14 +221,14 @@ dmdAnal dflags env dmd (Lam var body) dmdAnal dflags env dmd (Case scrut case_bndr ty [alt@(DataAlt dc, _, _)]) -- Only one alternative with a product constructor | let tycon = dataConTyCon dc - , isProductTyCon tycon + , isProductTyCon tycon , not (isRecursiveTyCon tycon) = let env_alt = extendAnalEnv NotTopLevel env case_bndr case_bndr_sig (alt_ty, alt') = dmdAnalAlt dflags env_alt dmd alt (alt_ty1, case_bndr') = annotateBndr alt_ty case_bndr (_, bndrs', _) = alt' - case_bndr_sig = cprSig + case_bndr_sig = cprProdSig -- Inside the alternative, the case binder has the CPR property. -- Meaning that a case on it will successfully cancel. -- Example: @@ -621,9 +621,11 @@ mkSigTy top_lvl rec_flag env id rhs (DmdType fv dmds res) strict_fv = filterUFM isStrictDmd fv ignore_cpr_info = not (exprIsHNF rhs || thunk_cpr_ok) - res' = if returnsCPR res && ignore_cpr_info - then topRes - else res + res' | returnsCPR res + , not (isTopLevel top_lvl || returnsCPRProd res) + -- See Note [CPR for sum types ] + || ignore_cpr_info = topRes + | otherwise = res -- Is it okay or not to assign CPR -- (not okay in the first pass) @@ -637,6 +639,32 @@ mkSigTy top_lvl rec_flag env id rhs (DmdType fv dmds res) | otherwise = False \end{code} +Note [CPR for sum types] +~~~~~~~~~~~~~~~~~~~~~~~~ +At the moment we do not do CPR for let-bindings that + * non-top level + * bind a sum type +Reason: I found that in some benchmarks we were losing let-no-escapes, +which messed it all up. Example + let j = \x. .... + in case y of + True -> j False + False -> j True +If we w/w this we get + let j' = \x. .... + in case y of + True -> case j False of { (# a #) -> Just a } + True -> case j True of { (# a #) -> Just a } +Notice that j' is not a let-no-escape any more. + +However this means in turn that the *enclosing* function +may be CPR'd (via the returned Justs). But in the case of +sums, there may be Nothing alterantives; and that messes +up the sum-type CPR. + +Conclusion: only do this for products. It's still not +guaranteed OK for products, but sums definitely lose sometimes. + Note [CPR for thunks] ~~~~~~~~~~~~~~~~~~~~~ If the rhs is a thunk, we usually forget the CPR info, because @@ -867,13 +895,11 @@ nonVirgin sigs = AE { ae_sigs = sigs, ae_virgin = False } extendSigsWithLam :: AnalEnv -> Id -> AnalEnv -- Extend the AnalEnv when we meet a lambda binder extendSigsWithLam env id - | ae_virgin env -- See Note [Optimistic CPR in the "virgin" case] - = extendAnalEnv NotTopLevel env id cprSig - - | isStrictDmd dmd_info -- Might be bottom, first time round - , Just {} <- deepSplitProductType_maybe $ idType id - = extendAnalEnv NotTopLevel env id cprSig + | isStrictDmd dmd_info || ae_virgin env + -- See Note [Optimistic CPR in the "virgin" case] -- See Note [Initial CPR for strict binders] + , Just {} <- deepSplitProductType_maybe $ idType id + = extendAnalEnv NotTopLevel env id cprProdSig | otherwise = env where @@ -882,7 +908,6 @@ extendSigsWithLam env id Note [Initial CPR for strict binders] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - CPR is initialized for a lambda binder in an optimistic manner, i.e, if the binder is used strictly and at least some of its components as a product are used, which is checked by the value of the absence diff --git a/compiler/stranal/WwLib.lhs b/compiler/stranal/WwLib.lhs index 1cbebf8c23..ea2365555b 100644 --- a/compiler/stranal/WwLib.lhs +++ b/compiler/stranal/WwLib.lhs @@ -16,26 +16,26 @@ module WwLib ( mkWwBodies, mkWWstr, mkWorkerArgs, deepSplitProductType_maybe ) w #include "HsVersions.h" import CoreSyn -import CoreUtils ( exprType ) +import CoreUtils ( exprType, mkCast ) import Id ( Id, idType, mkSysLocal, idDemandInfo, setIdDemandInfo, isOneShotLambda, setOneShotLambda, setIdUnfolding, - setIdInfo, setIdType + setIdInfo ) import IdInfo ( vanillaIdInfo ) import DataCon import Demand import MkCore ( mkRuntimeErrorApp, aBSENT_ERROR_ID ) -import MkId ( realWorldPrimId, voidArgId - , wrapNewTypeBody, unwrapNewTypeBody ) +import MkId ( realWorldPrimId, voidArgId ) import TysPrim ( realWorldStatePrimTy ) import TysWiredIn ( tupleCon ) import Type -import Coercion ( mkSymCo, instNewTyCon_maybe, splitNewTypeRepCo_maybe ) +import Coercion hiding ( substTy, substTyVarBndr ) import BasicTypes ( TupleSort(..) ) import Literal ( absentLiteralOf ) import TyCon import UniqSupply import Unique +import Maybes import Util import Outputable import DynFlags @@ -424,15 +424,16 @@ mkWWstr_one dflags arg -- Unpack case, -- see note [Unpacking arguments with product and polymorphic demands] | isStrictDmd dmd - , Just (_arg_tycon, _tycon_arg_tys, data_con, inst_con_arg_tys) + , Just cs <- splitProdDmd_maybe dmd + , Just (data_con, inst_tys, inst_con_arg_tys, co) <- deepSplitProductType_maybe (idType arg) - = do { uniqs <- getUniquesM - ; let cs = splitProdDmd (length inst_con_arg_tys) dmd - unpk_args = zipWith mk_ww_local uniqs inst_con_arg_tys + = do { (uniq1:uniqs) <- getUniquesM + ; let unpk_args = zipWith mk_ww_local uniqs inst_con_arg_tys unpk_args_w_ds = zipWithEqual "mkWWstr" set_worker_arg_info unpk_args cs - unbox_fn = mkUnpackCase (sanitiseCaseBndr arg) (Var arg) unpk_args data_con + unbox_fn = mkUnpackCase (Var arg `mkCast` co) uniq1 + data_con unpk_args rebox_fn = Let (NonRec arg con_app) - con_app = mkProductBox unpk_args (idType arg) + con_app = mkConApp2 data_con inst_tys unpk_args `mkCast` mkSymCo co ; (worker_args, wrap_fn, work_fn) <- mkWWstr dflags unpk_args_w_ds ; return (worker_args, unbox_fn . wrap_fn, work_fn . rebox_fn) } -- Don't pass the arg, rebox instead @@ -456,57 +457,25 @@ nop_fn body = body \end{code} \begin{code} -mkUnpackCase :: Id -> CoreExpr -> [Id] -> DataCon -> CoreExpr -> CoreExpr --- (mkUnpackCase x e args Con body) --- returns --- case (e `cast` ...) of bndr { Con args -> body } --- --- the type of the bndr passed in is irrelevent -mkUnpackCase bndr arg unpk_args boxing_con body - = Case cast_arg (setIdType bndr bndr_ty) (exprType body) [(DataAlt boxing_con, unpk_args, body)] - where - (cast_arg, bndr_ty) = go (idType bndr) arg - go ty arg - | (tycon, tycon_args, _, _) <- splitProductType "mkUnpackCase" ty - , isNewTyCon tycon && not (isRecursiveTyCon tycon) - = go (newTyConInstRhs tycon tycon_args) - (unwrapNewTypeBody tycon tycon_args arg) - | otherwise = (arg, ty) - -mkProductBox :: [Id] -> Type -> CoreExpr -mkProductBox arg_ids ty - = result_expr - where - (tycon, tycon_args, pack_con, _con_arg_tys) = splitProductType "mkProductBox" ty - - result_expr - | isNewTyCon tycon && not (isRecursiveTyCon tycon) - = wrap (mkProductBox arg_ids (newTyConInstRhs tycon tycon_args)) - | otherwise = mkConApp pack_con (map Type tycon_args ++ varsToCoreExprs arg_ids) - - wrap expr = wrapNewTypeBody tycon tycon_args expr - --- | As 'splitProductType_maybe', but in turn instantiates the 'TyCon' returned --- and hence recursively tries to unpack it as far as it able to -deepSplitProductType_maybe :: Type -> Maybe (TyCon, [Type], DataCon, [Type]) +deepSplitProductType_maybe :: Type -> Maybe (DataCon, [Type], [Type], Coercion) +-- If deepSplitProductType_maybe ty = Just (dc, tys, arg_tys, co) +-- then dc @ tys (args::arg_tys) |> co :: ty deepSplitProductType_maybe ty - = do { (res@(tycon, tycon_args, _, _)) <- splitProductType_maybe ty - ; let {result - | Just (ty', _co) <- instNewTyCon_maybe tycon tycon_args - , not (isRecursiveTyCon tycon) - = deepSplitProductType_maybe ty' -- Ignore the coercion? - | isNewTyCon tycon = Nothing -- cannot unbox through recursive - -- newtypes nor through families - | otherwise = Just res} - ; result - } - --- | As 'deepSplitProductType_maybe', but panics if the 'Type' is not a product type -deepSplitProductType :: String -> Type -> (TyCon, [Type], DataCon, [Type]) -deepSplitProductType str ty - = case deepSplitProductType_maybe ty of - Just stuff -> stuff - Nothing -> pprPanic (str ++ ": not a product") (pprType ty) + | let (ty1, co) = topNormaliseNewType ty `orElse` (ty, mkReflCo ty) + , Just (tc, tc_args) <- splitTyConApp_maybe ty1 + , Just con <- isDataProductTyCon_maybe tc + = Just (con, tc_args, dataConInstArgTys con tc_args, co) +deepSplitProductType_maybe _ = Nothing + +deepSplitCprType_maybe :: ConTag -> Type -> Maybe (DataCon, [Type], [Type], Coercion) +deepSplitCprType_maybe con_tag ty + | let (ty1, co) = topNormaliseNewType ty `orElse` (ty, mkReflCo ty) + , Just (tc, tc_args) <- splitTyConApp_maybe ty1 + , isDataTyCon tc + , let cons = tyConDataCons tc + con = ASSERT( cons `lengthAtLeast` con_tag ) cons !! (con_tag - fIRST_TAG) + = Just (con, tc_args, dataConInstArgTys con tc_args, co) +deepSplitCprType_maybe _ _ = Nothing \end{code} @@ -534,72 +503,79 @@ mkWWcpr :: Type -- function body type Type) -- Type of worker's body mkWWcpr body_ty res - | not (returnsCPR res) -- No CPR info - = return (id, id, body_ty) - - | not (isClosedAlgType body_ty) - = WARN( True, - text "mkWWcpr: non-algebraic or open body type" <+> ppr body_ty ) - return (id, id, body_ty) - - | n_con_args == 1 && isUnLiftedType con_arg_ty1 = do + = case returnsCPR_maybe res of + Nothing -> return (id, id, body_ty) -- No CPR info + Just con_tag | Just stuff <- deepSplitCprType_maybe con_tag body_ty + -> mkWWcpr_help stuff + | otherwise + -> WARN( True, text "mkWWcpr: non-algebraic or open body type" <+> ppr body_ty ) + return (id, id, body_ty) + +mkWWcpr_help :: (DataCon, [Type], [Type], Coercion) + -> UniqSM (CoreExpr -> CoreExpr, CoreExpr -> CoreExpr, Type) + +mkWWcpr_help (data_con, inst_tys, arg_tys, co) + | [arg_ty1] <- arg_tys + , isUnLiftedType arg_ty1 -- Special case when there is a single result of unlifted type -- -- Wrapper: case (..call worker..) of x -> C x -- Worker: case ( ..body.. ) of C x -> x - (work_uniq : arg_uniq : _) <- getUniquesM - let - work_wild = mk_ww_local work_uniq body_ty - arg = mk_ww_local arg_uniq con_arg_ty1 - con_app = mkProductBox [arg] body_ty + = do { (work_uniq : arg_uniq : _) <- getUniquesM + ; let arg = mk_ww_local arg_uniq arg_ty1 + con_app = mkConApp2 data_con inst_tys [arg] `mkCast` co - return (\ wkr_call -> Case wkr_call (arg) (exprType con_app) [(DEFAULT, [], con_app)], - \ body -> workerCase (work_wild) body [arg] data_con (Var arg), - con_arg_ty1) + ; return ( \ wkr_call -> Case wkr_call arg (exprType con_app) [(DEFAULT, [], con_app)] + , \ body -> mkUnpackCase body work_uniq data_con [arg] (Var arg) + , arg_ty1 ) } - | otherwise = do -- The general case + | otherwise -- The general case -- Wrapper: case (..call worker..) of (# a, b #) -> C a b -- Worker: case ( ...body... ) of C a b -> (# a, b #) - uniqs <- getUniquesM - let - (wrap_wild : work_wild : args) = zipWith mk_ww_local uniqs (ubx_tup_ty : body_ty : con_arg_tys) - arg_vars = varsToCoreExprs args - ubx_tup_con = tupleCon UnboxedTuple n_con_args - ubx_tup_ty = exprType ubx_tup_app - ubx_tup_app = mkConApp ubx_tup_con (map Type con_arg_tys ++ arg_vars) - con_app = mkProductBox args body_ty - - return (\ wkr_call -> Case wkr_call (wrap_wild) (exprType con_app) [(DataAlt ubx_tup_con, args, con_app)], - \ body -> workerCase (work_wild) body args data_con ubx_tup_app, - ubx_tup_ty) - where - (_arg_tycon, _tycon_arg_tys, data_con, con_arg_tys) = deepSplitProductType "mkWWcpr" body_ty - n_con_args = length con_arg_tys - con_arg_ty1 = head con_arg_tys - --- If the original function looked like --- f = \ x -> _scc_ "foo" E --- --- then we want the CPR'd worker to look like --- \ x -> _scc_ "foo" (case E of I# x -> x) --- and definitely not --- \ x -> case (_scc_ "foo" E) of I# x -> x) --- --- This transform doesn't move work or allocation --- from one cost centre to another. --- --- Later [SDM]: presumably this is because we want the simplifier to --- eliminate the case, and the scc would get in the way? I'm ok with --- including the case itself in the cost centre, since it is morally --- part of the function (post transformation) anyway. - -workerCase :: Id -> CoreExpr -> [Id] -> DataCon -> CoreExpr -> CoreExpr -workerCase bndr (Tick tickish e) args con body - = Tick tickish (mkUnpackCase bndr e args con body) -workerCase bndr e args con body - = mkUnpackCase bndr e args con body + = do { (work_uniq : uniqs) <- getUniquesM + ; let (wrap_wild : args) = zipWith mk_ww_local uniqs (ubx_tup_ty : arg_tys) + ubx_tup_con = tupleCon UnboxedTuple (length arg_tys) + ubx_tup_ty = exprType ubx_tup_app + ubx_tup_app = mkConApp2 ubx_tup_con arg_tys args + con_app = mkConApp2 data_con inst_tys args `mkCast` co + + ; return ( \ wkr_call -> Case wkr_call wrap_wild (exprType con_app) [(DataAlt ubx_tup_con, args, con_app)] + , \ body -> mkUnpackCase body work_uniq data_con args ubx_tup_app + , ubx_tup_ty ) } + +mkUnpackCase :: CoreExpr -> Unique -> DataCon -> [Id] -> CoreExpr -> CoreExpr +-- (mkUnpackCase e bndr Con args body) +-- returns +-- case e of bndr { Con args -> body } +-- +-- the type of the bndr passed in is irrelevent + +mkUnpackCase (Tick tickish e) uniq con args body -- See Note [Profiling and unpacking] + = Tick tickish (mkUnpackCase e uniq con args body) +mkUnpackCase scrut uniq boxing_con unpk_args body + = Case scrut + (mk_ww_local uniq (exprType scrut)) (exprType body) + [(DataAlt boxing_con, unpk_args, body)] \end{code} +Note [Profiling and unpacking] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If the original function looked like + f = \ x -> _scc_ "foo" E + +then we want the CPR'd worker to look like + \ x -> _scc_ "foo" (case E of I# x -> x) +and definitely not + \ x -> case (_scc_ "foo" E) of I# x -> x) + +This transform doesn't move work or allocation +from one cost centre to another. + +Later [SDM]: presumably this is because we want the simplifier to +eliminate the case, and the scc would get in the way? I'm ok with +including the case itself in the cost centre, since it is morally +part of the function (post transformation) anyway. + %************************************************************************ %* * diff --git a/compiler/types/Coercion.lhs b/compiler/types/Coercion.lhs index 83f31af3af..ecf4e3a9e3 100644 --- a/compiler/types/Coercion.lhs +++ b/compiler/types/Coercion.lhs @@ -36,9 +36,10 @@ module Coercion ( mkNewTypeCo, -- ** Decomposition - splitNewTypeRepCo_maybe, instNewTyCon_maybe, decomposeCo, - getCoVar_maybe, + splitNewTypeRepCo_maybe, instNewTyCon_maybe, + topNormaliseNewType, topNormaliseNewTypeX, + decomposeCo, getCoVar_maybe, splitTyConAppCo_maybe, splitAppCo_maybe, splitForAllCo_maybe, @@ -88,6 +89,7 @@ import VarEnv import VarSet import Maybes ( orElse ) import Name ( Name, NamedThing(..), nameUnique, getSrcSpan ) +import NameSet import OccName ( parenSymOcc ) import Util import BasicTypes @@ -745,34 +747,64 @@ instNewTyCon_maybe :: TyCon -> [Type] -> Maybe (Type, Coercion) -- ^ If @co :: T ts ~ rep_ty@ then: -- -- > instNewTyCon_maybe T ts = Just (rep_ty, co) +-- Checks for a newtype, and for being saturated instNewTyCon_maybe tc tys - | Just (tvs, ty, co_tc) <- unwrapNewTyCon_maybe tc - = ASSERT( tys `lengthIs` tyConArity tc ) - Just (substTyWith tvs tys ty, mkUnbranchedAxInstCo co_tc tys) + | Just (tvs, ty, co_tc) <- unwrapNewTyCon_maybe tc -- Check for newtype + , tys `lengthIs` tyConArity tc -- Check saturated + = Just (substTyWith tvs tys ty, mkUnbranchedAxInstCo co_tc tys) | otherwise = Nothing --- this is here to avoid module loops splitNewTypeRepCo_maybe :: Type -> Maybe (Type, Coercion) -- ^ Sometimes we want to look through a @newtype@ and get its associated coercion. -- This function only strips *one layer* of @newtype@ off, so the caller will usually call --- itself recursively. Furthermore, this function should only be applied to types of kind @*@, --- hence the newtype is always saturated. If @co : ty ~ ty'@ then: +-- itself recursively. If -- -- > splitNewTypeRepCo_maybe ty = Just (ty', co) -- --- The function returns @Nothing@ for non-@newtypes@ or fully-transparent @newtype@s. +-- then @co : ty ~ ty'@. The function returns @Nothing@ for non-@newtypes@, +-- or unsaturated applications splitNewTypeRepCo_maybe ty - | Just ty' <- coreView ty = splitNewTypeRepCo_maybe ty' + | Just ty' <- coreView ty + = splitNewTypeRepCo_maybe ty' splitNewTypeRepCo_maybe (TyConApp tc tys) - | Just (ty', co) <- instNewTyCon_maybe tc tys - = case co of - Refl _ -> panic "splitNewTypeRepCo_maybe" - -- This case handled by coreView - _ -> Just (ty', co) + = instNewTyCon_maybe tc tys splitNewTypeRepCo_maybe _ = Nothing +topNormaliseNewType :: Type -> Maybe (Type, Coercion) +topNormaliseNewType ty + = case topNormaliseNewTypeX emptyNameSet ty of + Just (_, co, ty) -> Just (ty, co) + Nothing -> Nothing + +topNormaliseNewTypeX :: NameSet -> Type -> Maybe (NameSet, Coercion, Type) +topNormaliseNewTypeX rec_nts ty + | Just ty' <- coreView ty -- Expand predicates and synonyms + = topNormaliseNewTypeX rec_nts ty' + +topNormaliseNewTypeX rec_nts (TyConApp tc tys) + | Just (rep_ty, co) <- instNewTyCon_maybe tc tys + , not (tc_name `elemNameSet` rec_nts) -- See Note [Expanding newtypes] in Type + = case topNormaliseNewTypeX rec_nts' rep_ty of + Nothing -> Just (rec_nts', co, rep_ty) + Just (rec_nts', co', rep_ty') -> Just (rec_nts', co `mkTransCo` co', rep_ty') + where + tc_name = tyConName tc + rec_nts' | isRecursiveTyCon tc = addOneToNameSet rec_nts tc_name + | otherwise = rec_nts + +topNormaliseNewTypeX _ _ = Nothing +\end{code} + + +%************************************************************************ +%* * + Equality of coercions +%* * +%************************************************************************ + +\begin{code} -- | Determines syntactic equality of coercions coreEqCoercion :: Coercion -> Coercion -> Bool coreEqCoercion co1 co2 = coreEqCoercion2 rn_env co1 co2 diff --git a/compiler/types/FamInstEnv.lhs b/compiler/types/FamInstEnv.lhs index 617cfa0b3a..f657b5bff6 100644 --- a/compiler/types/FamInstEnv.lhs +++ b/compiler/types/FamInstEnv.lhs @@ -47,6 +47,7 @@ import CoAxiom import VarSet import VarEnv import Name +import NameSet import UniqFM import Outputable import Maybes @@ -908,32 +909,26 @@ topNormaliseType :: FamInstEnvs -- Its a bit like Type.repType, but handles type families too topNormaliseType env ty - = go [] ty + = go emptyNameSet ty where - go :: [TyCon] -> Type -> Maybe (Coercion, Type) - go rec_nts ty | Just ty' <- coreView ty -- Expand synonyms + go :: NameSet -> Type -> Maybe (Coercion, Type) + go rec_nts ty + | Just ty' <- coreView ty -- Expand synonyms = go rec_nts ty' - go rec_nts (TyConApp tc tys) - | isNewTyCon tc -- Expand newtypes - = if tc `elem` rec_nts -- See Note [Expanding newtypes] in Type.lhs - then Nothing - else let - in add_co nt_co rec_nts' nt_rhs + | Just (rec_nts', nt_co, nt_rhs) <- topNormaliseNewTypeX rec_nts ty + = add_co nt_co rec_nts' nt_rhs + go rec_nts (TyConApp tc tys) | isFamilyTyCon tc -- Expand open tycons , (co, ty) <- normaliseTcApp env tc tys -- Note that normaliseType fully normalises 'tys', + -- wrt type functions but *not* newtypes -- It has do to so to be sure that nested calls like -- F (G Int) -- are correctly top-normalised , not (isReflCo co) = add_co co rec_nts ty - where - nt_co = mkUnbranchedAxInstCo (newTyConCo tc) tys - nt_rhs = newTyConInstRhs tc tys - rec_nts' | isRecursiveTyCon tc = tc:rec_nts - | otherwise = rec_nts go _ _ = Nothing @@ -962,7 +957,7 @@ normaliseTcApp env tc tys (fix_coi, nty) | otherwise -- No unique matching family instance exists; - -- we do not do anything + -- we do not do anything (including for newtypes) = (tycon_coi, TyConApp tc ntys) where @@ -978,6 +973,7 @@ normaliseType :: FamInstEnvs -- environment with family instances -- co :: old-type ~ new_type -- Normalise the input type, by eliminating *all* type-function redexes -- Returns with Refl if nothing happens +-- Does nothing to newtypes normaliseType env ty | Just ty' <- coreView ty = normaliseType env ty' diff --git a/compiler/types/TyCon.lhs b/compiler/types/TyCon.lhs index 0bce4db43e..ce14944011 100644 --- a/compiler/types/TyCon.lhs +++ b/compiler/types/TyCon.lhs @@ -41,7 +41,8 @@ module TyCon( isPromotedDataCon_maybe, isPromotedTyCon_maybe, isInjectiveTyCon, - isDataTyCon, isProductTyCon, isEnumerationTyCon, + isDataTyCon, isProductTyCon, isDataProductTyCon_maybe, + isEnumerationTyCon, isNewTyCon, isAbstractTyCon, isFamilyTyCon, isSynFamilyTyCon, isDataFamilyTyCon, isUnLiftedTyCon, @@ -1058,14 +1059,8 @@ unwrapNewTyCon_maybe (AlgTyCon { tyConTyVars = tvs, unwrapNewTyCon_maybe _ = Nothing isProductTyCon :: TyCon -> Bool --- | A /product/ 'TyCon' must both: --- --- 1. Have /one/ constructor --- --- 2. /Not/ be existential --- --- However other than this there are few restrictions: they may be @data@ or @newtype@ --- 'TyCon's of any boxity and may even be recursive. +-- True of datatypes or newtypes that have +-- one, vanilla, data constructor isProductTyCon tc@(AlgTyCon {}) = case algTcRhs tc of DataTyCon{ data_cons = [data_con] } -> isVanillaDataCon data_con @@ -1074,6 +1069,18 @@ isProductTyCon tc@(AlgTyCon {}) = case algTcRhs tc of isProductTyCon (TupleTyCon {}) = True isProductTyCon _ = False + +isDataProductTyCon_maybe :: TyCon -> Maybe DataCon +-- True of datatypes (not newtypes) with +-- one, vanilla, data constructor +isDataProductTyCon_maybe (AlgTyCon { algTcRhs = DataTyCon { data_cons = cons } }) + | [con] <- cons -- Singleton + , isVanillaDataCon con -- Vanilla + = Just con +isDataProductTyCon_maybe (TupleTyCon { dataCon = con }) + = Just con +isDataProductTyCon_maybe _ = Nothing + -- | Is this a 'TyCon' representing a type synonym (@type@)? isSynTyCon :: TyCon -> Bool isSynTyCon (SynTyCon {}) = True diff --git a/compiler/types/Type.lhs b/compiler/types/Type.lhs index efe8a3bde3..3cab277dc4 100644 --- a/compiler/types/Type.lhs +++ b/compiler/types/Type.lhs @@ -49,7 +49,7 @@ module Type ( coAxNthLHS, -- (Newtypes) - newTyConInstRhs, carefullySplitNewType_maybe, + newTyConInstRhs, -- Pred types mkFamilyTyConApp, @@ -657,8 +657,13 @@ repType ty = go rec_nts ty go rec_nts (TyConApp tc tys) -- Expand newtypes - | Just (rec_nts', ty') <- carefullySplitNewType_maybe rec_nts tc tys - = go rec_nts' ty' + | isNewTyCon tc + , tys `lengthAtLeast` tyConArity tc + , let tc_name = tyConName tc + rec_nts' | isRecursiveTyCon tc = addOneToNameSet rec_nts tc_name + | otherwise = rec_nts + , not (tc_name `elemNameSet` rec_nts) -- See Note [Expanding newtypes] + = go rec_nts' (newTyConInstRhs tc tys) | isUnboxedTupleTyCon tc = if null tys @@ -667,21 +672,6 @@ repType ty go _ ty = UnaryRep ty -carefullySplitNewType_maybe :: NameSet -> TyCon -> [Type] -> Maybe (NameSet,Type) --- Return the representation of a newtype, unless --- we've seen it already: see Note [Expanding newtypes] --- Assumes the newtype is saturated -carefullySplitNewType_maybe rec_nts tc tys - | isNewTyCon tc - , tys `lengthAtLeast` tyConArity tc - , not (tc_name `elemNameSet` rec_nts) = Just (rec_nts', newTyConInstRhs tc tys) - | otherwise = Nothing - where - tc_name = tyConName tc - rec_nts' | isRecursiveTyCon tc = addOneToNameSet rec_nts tc_name - | otherwise = rec_nts - - -- ToDo: this could be moved to the code generator, using splitTyConApp instead -- of inspecting the type directly. diff --git a/compiler/vectorise/Vectorise/Exp.hs b/compiler/vectorise/Vectorise/Exp.hs index 20fade521b..0ae4fbf05f 100644 --- a/compiler/vectorise/Vectorise/Exp.hs +++ b/compiler/vectorise/Vectorise/Exp.hs @@ -661,11 +661,11 @@ unVectDict ty e ; return $ mkCoreConApps dataCon (map Type tys ++ scOps) } where - (tycon, tys, dataCon, methTys) = splitProductType "unVectDict: original type" ty - cls = case tyConClass_maybe tycon of - Just cls -> cls - Nothing -> panic "Vectorise.Exp.unVectDict: no class" - selIds = classAllSelIds cls + (tycon, tys) = splitTyConApp ty + Just dataCon = isDataProductTyCon_maybe tycon + Just cls = tyConClass_maybe tycon + methTys = dataConInstArgTys dataCon tys + selIds = classAllSelIds cls -- Vectorise an 'n'-ary lambda abstraction by building a set of 'n' explicit closures. -- |