[commit: ghc] master: Improve LiberateCase (800009d)

git at git.haskell.org git at git.haskell.org
Fri Dec 8 17:21:08 UTC 2017


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/800009d9b78a9b2877e7efc889e8a0b21873990d/ghc

>---------------------------------------------------------------

commit 800009d9b78a9b2877e7efc889e8a0b21873990d
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date:   Fri Dec 8 15:31:36 2017 +0000

    Improve LiberateCase
    
    This patch, which fixes Trac #14566, makes LiberateCase a little
    more conservative.  In particular:
    
    * In libCaseBind, treat a recursive group as a whole, rather than
      binding-by-binding, allowing the group to be duplicated only if
        - the bindings /considered together/ are smaller than the
          liberate-case threshold (which is large by default)
        - none of them are thunks
        - none of them are guaranteed-diverging
    
      The latter condidtion is new, and happens to apply in the
      case of Data/Typeable/Internal.mkTrApp


>---------------------------------------------------------------

800009d9b78a9b2877e7efc889e8a0b21873990d
 compiler/simplCore/LiberateCase.hs | 90 +++++++++++++++++++++++---------------
 1 file changed, 54 insertions(+), 36 deletions(-)

diff --git a/compiler/simplCore/LiberateCase.hs b/compiler/simplCore/LiberateCase.hs
index 8cee064..342ad73 100644
--- a/compiler/simplCore/LiberateCase.hs
+++ b/compiler/simplCore/LiberateCase.hs
@@ -14,6 +14,7 @@ import GhcPrelude
 import DynFlags
 import CoreSyn
 import CoreUnfold       ( couldBeSmallEnoughToInline )
+import TysWiredIn       ( unitDataConId )
 import Id
 import VarEnv
 import Util             ( notNull )
@@ -68,24 +69,6 @@ Exactly the same optimisation (unrolling one call to f) will work here,
 despite the cast.  See mk_alt_env in the Case branch of libCase.
 
 
-Note [Only functions!]
-~~~~~~~~~~~~~~~~~~~~~~
-Consider the following code
-
-       f = g (case v of V a b -> a : t f)
-
-where g is expensive. If we aren't careful, liberate case will turn this into
-
-       f = g (case v of
-               V a b -> a : t (letrec f = g (case v of V a b -> a : f t)
-                                in f)
-             )
-
-Yikes! We evaluate g twice. This leads to a O(2^n) explosion
-if g calls back to the same code recursively.
-
-Solution: make sure that we only do the liberate-case thing on *functions*
-
 To think about (Apr 94)
 ~~~~~~~~~~~~~~
 Main worry: duplicating code excessively.  At the moment we duplicate
@@ -156,18 +139,63 @@ libCaseBind env (Rec pairs)
         -- We extend the rec-env by binding each Id to its rhs, first
         -- processing the rhs with an *un-extended* environment, so
         -- that the same process doesn't occur for ever!
-    env_rhs = addRecBinds env [ (localiseId binder, libCase env_body rhs)
-                              | (binder, rhs) <- pairs
-                              , rhs_small_enough binder rhs ]
+    env_rhs | is_dupable_bind = addRecBinds env dup_pairs
+            | otherwise       = env
+
+    dup_pairs = [ (localiseId binder, libCase env_body rhs)
+                | (binder, rhs) <- pairs ]
         -- localiseID : see Note [Need to localiseId in libCaseBind]
 
+    is_dupable_bind = small_enough && all ok_pair pairs
 
-    rhs_small_enough id rhs     -- Note [Small enough]
-        =  idArity id > 0       -- Note [Only functions!]
-        && maybe True (\size -> couldBeSmallEnoughToInline (lc_dflags env) size rhs)
-                      (bombOutSize env)
+    -- Size: we are going to duplicate dup_pairs; to find their
+    --       size, build a fake binding (let { dup_pairs } in (),
+    --       and find the size of that
+    -- See Note [Small enough]
+    small_enough = case bombOutSize env of
+                      Nothing   -> True   -- Infinity
+                      Just size -> couldBeSmallEnoughToInline (lc_dflags env) size $
+                                   Let (Rec dup_pairs) (Var unitDataConId)
+
+    ok_pair (id,_)
+        =  idArity id > 0          -- Note [Only functions!]
+        && not (isBottomingId id)  -- Note [Not bottoming ids]
+
+{- Note [Not bottoming Ids]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Do not specialise error-functions (this is unusual, but I once saw it,
+(acually in Data.Typable.Internal)
+
+Note [Only functions!]
+~~~~~~~~~~~~~~~~~~~~~~
+Consider the following code
+
+       f = g (case v of V a b -> a : t f)
+
+where g is expensive. If we aren't careful, liberate case will turn this into
+
+       f = g (case v of
+               V a b -> a : t (letrec f = g (case v of V a b -> a : f t)
+                                in f)
+             )
+
+Yikes! We evaluate g twice. This leads to a O(2^n) explosion
+if g calls back to the same code recursively.
+
+Solution: make sure that we only do the liberate-case thing on *functions*
+
+Note [Small enough]
+~~~~~~~~~~~~~~~~~~~
+Consider
+  \fv. letrec
+         f = \x. BIG...(case fv of { (a,b) -> ...g.. })...
+         g = \y. SMALL...f...
+
+Then we *can* in principle do liberate-case on 'g' (small RHS) but not
+for 'f' (too big).  But doing so is not profitable, becuase duplicating
+'g' at its call site in 'f' doesn't get rid of any cases.  So we just
+ask for the whole group to be small enough.
 
-{-
 Note [Need to localiseId in libCaseBind]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 The call to localiseId is needed for two subtle reasons
@@ -181,16 +209,6 @@ The call to localiseId is needed for two subtle reasons
         nested; if it were floated to the top level, we'd get a name
         clash at code generation time.
 
-Note [Small enough]
-~~~~~~~~~~~~~~~~~~~
-Consider
-  \fv. letrec
-         f = \x. BIG...(case fv of { (a,b) -> ...g.. })...
-         g = \y. SMALL...f...
-Then we *can* do liberate-case on g (small RHS) but not for f (too big).
-But we can choose on a item-by-item basis, and that's what the
-rhs_small_enough call in the comprehension for env_rhs does.
-
 Expressions
 ~~~~~~~~~~~
 -}



More information about the ghc-commits mailing list