Commit 3521c507 authored by Simon Peyton Jones's avatar Simon Peyton Jones
Browse files

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.
parent ab4c27e9
......@@ -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
......
......@@ -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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment