[commit: ghc] master: When finding loop breakers, distinguish INLINE from INLINEABLE (3521c50)

git at git.haskell.org git at git.haskell.org
Fri Aug 29 16:17:18 UTC 2014


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

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/3521c5078dace81b23a72d1e463f9c31d07f3816/ghc

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

commit 3521c5078dace81b23a72d1e463f9c31d07f3816
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date:   Fri Aug 29 15:18:08 2014 +0100

    When finding loop breakers, distinguish INLINE from INLINEABLE
    
    Previously INLINE and INLINEABLE were treated identically, but it's
    crucial that we don't choose a wrapper (INLINE) as a loop breaker,
    when it is mutually recursive with an INLINEABLE worker.


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

3521c5078dace81b23a72d1e463f9c31d07f3816
 compiler/coreSyn/CoreSyn.lhs     | 17 +++++++++----
 compiler/simplCore/OccurAnal.lhs | 55 ++++++++++++++--------------------------
 2 files changed, 31 insertions(+), 41 deletions(-)

diff --git a/compiler/coreSyn/CoreSyn.lhs b/compiler/coreSyn/CoreSyn.lhs
index 6627ab0..babece4 100644
--- a/compiler/coreSyn/CoreSyn.lhs
+++ b/compiler/coreSyn/CoreSyn.lhs
@@ -58,7 +58,7 @@ module CoreSyn (
 	maybeUnfoldingTemplate, otherCons, 
 	isValueUnfolding, isEvaldUnfolding, isCheapUnfolding,
         isExpandableUnfolding, isConLikeUnfolding, isCompulsoryUnfolding,
-        isStableUnfolding, isStableCoreUnfolding_maybe,
+        isStableUnfolding, hasStableCoreUnfolding_maybe,
         isClosedUnfolding, hasSomeUnfolding, 
 	canUnfold, neverUnfoldGuidance, isStableSource,
 
@@ -929,10 +929,17 @@ expandUnfolding_maybe :: Unfolding -> Maybe CoreExpr
 expandUnfolding_maybe (CoreUnfolding { uf_expandable = True, uf_tmpl = rhs }) = Just rhs
 expandUnfolding_maybe _                                                       = Nothing
 
-isStableCoreUnfolding_maybe :: Unfolding -> Maybe UnfoldingSource
-isStableCoreUnfolding_maybe (CoreUnfolding { uf_src = src })
-   | isStableSource src   = Just src
-isStableCoreUnfolding_maybe _ = Nothing
+hasStableCoreUnfolding_maybe :: Unfolding -> Maybe Bool
+-- Just True  <=> has stable inlining, very keen to inline (eg. INLINE pragma)
+-- Just False <=> has stable inlining, open to inlining it (eg. INLINEABLE pragma)
+-- Nothing    <=> not table, or cannot inline it anyway
+hasStableCoreUnfolding_maybe (CoreUnfolding { uf_src = src, uf_guidance = guide })
+   | isStableSource src
+   = case guide of
+       UnfWhen {}       -> Just True
+       UnfIfGoodArgs {} -> Just False
+       UnfNever         -> Nothing
+hasStableCoreUnfolding_maybe _ = Nothing
 
 isCompulsoryUnfolding :: Unfolding -> Bool
 isCompulsoryUnfolding (CoreUnfolding { uf_src = InlineCompulsory }) = True
diff --git a/compiler/simplCore/OccurAnal.lhs b/compiler/simplCore/OccurAnal.lhs
index 7a237c8..ca0fc22 100644
--- a/compiler/simplCore/OccurAnal.lhs
+++ b/compiler/simplCore/OccurAnal.lhs
@@ -879,13 +879,14 @@ reOrderNodes depth bndr_set weak_fvs (node : nodes) binds
         | isDFunId bndr = 9   -- Never choose a DFun as a loop breaker
                               -- Note [DFuns should not be loop breakers]
 
-        | Just _ <- isStableCoreUnfolding_maybe (idUnfolding bndr)
-        = 3    -- Note [INLINE pragmas]
+        | Just be_very_keen <- hasStableCoreUnfolding_maybe (idUnfolding bndr)
+        = if be_very_keen then 6    -- Note [Loop breakers and INLINE/INLINEABLE pragmas]
+                          else 3
                -- Data structures are more important than INLINE pragmas
                -- so that dictionary/method recursion unravels
-               -- Note that this case hits all InlineRule things, so we
-               -- never look at 'rhs' for InlineRule stuff. That's right, because
-               -- 'rhs' is irrelevant for inlining things with an InlineRule
+               -- Note that this case hits all stable unfoldings, so we
+               -- never look at 'rhs' for stable unfoldings. That's right, because
+               -- 'rhs' is irrelevant for inlining things with a stable unfolding
 
         | is_con_app rhs = 5  -- Data types help with cases: Note [Constructor applications]
 
@@ -962,43 +963,25 @@ The RULES stuff means that we can't choose $dm as a loop breaker
 opInt *and* opBool, and so on.  The number of loop breakders is
 linear in the number of instance declarations.
 
-Note [INLINE pragmas]
-~~~~~~~~~~~~~~~~~~~~~
+Note [Loop breakers and INLINE/INLINEABLE pragmas]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Avoid choosing a function with an INLINE pramga as the loop breaker!
 If such a function is mutually-recursive with a non-INLINE thing,
 then the latter should be the loop-breaker.
 
-   ----> Historical note, dating from when strictness wrappers
-   were generated from the strictness signatures:
+It's vital to distinguish between INLINE and INLINEABLE (the
+Bool returned by hasStableCoreUnfolding_maybe).  If we start with
+   Rec { {-# INLINEABLE f #-}
+         f x = ...f... }
+and then worker/wrapper it through strictness analysis, we'll get
+   Rec { {-# INLINEABLE $wf #-}
+         $wf p q = let x = (p,q) in ...f...
 
-      Usually this is just a question of optimisation. But a particularly
-      bad case is wrappers generated by the demand analyser: if you make
-      then into a loop breaker you may get an infinite inlining loop.  For
-      example:
-        rec {
-              $wfoo x = ....foo x....
+         {-# INLINE f #-}
+         f x = case x of (p,q) -> $wf p q }
 
-              {-loop brk-} foo x = ...$wfoo x...
-        }
-      The interface file sees the unfolding for $wfoo, and sees that foo is
-      strict (and hence it gets an auto-generated wrapper).  Result: an
-      infinite inlining in the importing scope.  So be a bit careful if you
-      change this.  A good example is Tree.repTree in
-      nofib/spectral/minimax. If the repTree wrapper is chosen as the loop
-      breaker then compiling Game.hs goes into an infinite loop.  This
-      happened when we gave is_con_app a lower score than inline candidates:
-
-        Tree.repTree
-          = __inline_me (/\a. \w w1 w2 ->
-                         case Tree.$wrepTree @ a w w1 w2 of
-                          { (# ww1, ww2 #) -> Branch @ a ww1 ww2 })
-        Tree.$wrepTree
-          = /\a w w1 w2 ->
-            (# w2_smP, map a (Tree a) (Tree.repTree a w1 w) (w w2) #)
-
-      Here we do *not* want to choose 'repTree' as the loop breaker.
-
-   -----> End of historical note
+Now it is vital that we choose $wf as the loop breaker, so we can
+inline 'f' in '$wf'.
 
 Note [DFuns should not be loop breakers]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



More information about the ghc-commits mailing list