diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2019-11-08 17:49:35 +0000 |
---|---|---|
committer | Alp Mestanogullari <alp@well-typed.com> | 2020-04-01 03:20:38 -0400 |
commit | 812c475056e4e16b93ba1fa79d8b44b1329759b1 (patch) | |
tree | 0ecae73b7a38d6068a18214b73fd94bae16db16a /compiler/GHC/Core/Utils.hs | |
parent | 0002db1bf436cbd32f97b659a52b1eee4e8b21db (diff) | |
download | haskell-wip/T16296.tar.gz |
Re-engineer the binder-swap transformationwip/T16296
The binder-swap transformation is implemented by the occurrence
analyser -- see Note [Binder swap] in OccurAnal. However it had
a very nasty corner in it, for the case where the case scrutinee
was a GlobalId. This led to trouble and hacks, and ultimately
to #16296.
This patch re-engineers how the occurrence analyser implements
the binder-swap, by actually carrying out a substitution rather
than by adding a let-binding. It's all described in
Note [The binder-swap substitution].
I did a few other things along the way
* Fix a bug in StgCse, which could allow a loop breaker to be CSE'd
away. See Note [Care with loop breakers] in StgCse. I think it can
only show up if occurrence analyser sets up bad loop breakers, but
still.
* Better commenting in SimplUtils.prepareAlts
* A little refactoring in CoreUnfold; nothing significant
e.g. rename CoreUnfold.mkTopUnfolding to mkFinalUnfolding
* Renamed CoreSyn.isFragileUnfolding to hasCoreUnfolding
* Move mkRuleInfo to CoreFVs
We observed respectively 4.6% and 5.9% allocation decreases for the following
tests:
Metric Decrease:
T9961
haddock.base
Diffstat (limited to 'compiler/GHC/Core/Utils.hs')
-rw-r--r-- | compiler/GHC/Core/Utils.hs | 139 |
1 files changed, 53 insertions, 86 deletions
diff --git a/compiler/GHC/Core/Utils.hs b/compiler/GHC/Core/Utils.hs index 4663f54b26..526ba34fd0 100644 --- a/compiler/GHC/Core/Utils.hs +++ b/compiler/GHC/Core/Utils.hs @@ -696,7 +696,7 @@ filterAlts _tycon inst_tys imposs_cons alts impossible_alt _ _ = False -- | Refine the default alternative to a 'DataAlt', if there is a unique way to do so. --- See Note [Refine Default Alts] +-- See Note [Refine DEFAULT case alternatives] refineDefaultAlt :: [Unique] -- ^ Uniques for constructing new binders -> TyCon -- ^ Type constructor of scrutinee's type -> [Type] -- ^ Type arguments of scrutinee's type @@ -739,95 +739,62 @@ refineDefaultAlt us tycon tys imposs_deflt_cons all_alts | otherwise -- The common case = (False, all_alts) -{- Note [Refine Default Alts] - -refineDefaultAlt replaces the DEFAULT alt with a constructor if there is one -possible value it could be. +{- Note [Refine DEFAULT case alternatives] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +refineDefaultAlt replaces the DEFAULT alt with a constructor if there +is one possible value it could be. The simplest example being + foo :: () -> () + foo x = case x of !_ -> () +which rewrites to + foo :: () -> () + foo x = case x of () -> () + +There are two reasons in general why replacing a DEFAULT alternative +with a specific constructor is desirable. + +1. We can simplify inner expressions. For example + + data Foo = Foo1 () + + test :: Foo -> () + test x = case x of + DEFAULT -> mid (case x of + Foo1 x1 -> x1) + + refineDefaultAlt fills in the DEFAULT here with `Foo ip1` and then + x becomes bound to `Foo ip1` so is inlined into the other case + which causes the KnownBranch optimisation to kick in. If we don't + refine DEFAULT to `Foo ip1`, we are left with both case expressions. + +2. combineIdenticalAlts does a better job. For exapple (Simon Jacobi) + data D = C0 | C1 | C2 + + case e of + DEFAULT -> e0 + C0 -> e1 + C1 -> e1 + + When we apply combineIdenticalAlts to this expression, it can't + combine the alts for C0 and C1, as we already have a default case. + But if we apply refineDefaultAlt first, we get + case e of + C0 -> e1 + C1 -> e1 + C2 -> e0 + and combineIdenticalAlts can turn that into + case e of + DEFAULT -> e1 + C2 -> e0 -foo :: () -> () -foo x = case x of !_ -> () - -rewrites to - -foo :: () -> () -foo x = case x of () -> () - -There are two reasons in general why this is desirable. - -1. We can simplify inner expressions - -In this example we can eliminate the inner case by refining the outer case. -If we don't refine it, we are left with both case expressions. - -``` -{-# LANGUAGE BangPatterns #-} -module Test where - -mid x = x -{-# NOINLINE mid #-} - -data Foo = Foo1 () - -test :: Foo -> () -test x = - case x of - !_ -> mid (case x of - Foo1 x1 -> x1) - -``` - -refineDefaultAlt fills in the DEFAULT here with `Foo ip1` and then x -becomes bound to `Foo ip1` so is inlined into the other case which -causes the KnownBranch optimisation to kick in. - - -2. combineIdenticalAlts does a better job - -Simon Jakobi also points out that that combineIdenticalAlts will do a better job -if we refine the DEFAULT first. - -``` -data D = C0 | C1 | C2 - -case e of - DEFAULT -> e0 - C0 -> e1 - C1 -> e1 -``` - -When we apply combineIdenticalAlts to this expression, it can't -combine the alts for C0 and C1, as we already have a default case. - -If we apply refineDefaultAlt first, we get - -``` -case e of - C0 -> e1 - C1 -> e1 - C2 -> e0 -``` - -and combineIdenticalAlts can turn that into - -``` -case e of - DEFAULT -> e1 - C2 -> e0 -``` - -It isn't obvious that refineDefaultAlt does this but if you look at its one call -site in GHC.Core.Op.Simplify.Utils then the `imposs_deflt_cons` argument is -populated with constructors which are matched elsewhere. - --} - - - + It isn't obvious that refineDefaultAlt does this but if you look + at its one call site in GHC.Core.Op.Simplify.Utils then the + `imposs_deflt_cons` argument is populated with constructors which + are matched elsewhere. -{- Note [Combine identical alternatives] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note [Combine identical alternatives] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If several alternatives are identical, merge them into a single DEFAULT alternative. I've occasionally seen this making a big difference: |