diff options
author | simonpj@microsoft.com <unknown> | 2008-12-05 16:54:00 +0000 |
---|---|---|
committer | simonpj@microsoft.com <unknown> | 2008-12-05 16:54:00 +0000 |
commit | d95ce839533391e7118257537044f01cbb1d6694 (patch) | |
tree | f0721012658d593367a60c890bb283465da7b339 /compiler/stranal | |
parent | ccd0e382566940a508fcb1aa7487bc7a785fc329 (diff) | |
download | haskell-d95ce839533391e7118257537044f01cbb1d6694.tar.gz |
Completely new treatment of INLINE pragmas (big patch)
This is a major patch, which changes the way INLINE pragmas work.
Although lots of files are touched, the net is only +21 lines of
code -- and I bet that most of those are comments!
HEADS UP: interface file format has changed, so you'll need to
recompile everything.
There is not much effect on overall performance for nofib,
probably because those programs don't make heavy use of INLINE pragmas.
Program Size Allocs Runtime Elapsed
Min -11.3% -6.9% -9.2% -8.2%
Max -0.1% +4.6% +7.5% +8.9%
Geometric Mean -2.2% -0.2% -1.0% -0.8%
(The +4.6% for on allocs is cichelli; see other patch relating to
-fpass-case-bndr-to-join-points.)
The old INLINE system
~~~~~~~~~~~~~~~~~~~~~
The old system worked like this. A function with an INLINE pragam
got a right-hand side which looked like
f = __inline_me__ (\xy. e)
The __inline_me__ part was an InlineNote, and was treated specially
in various ways. Notably, the simplifier didn't inline inside an
__inline_me__ note.
As a result, the code for f itself was pretty crappy. That matters
if you say (map f xs), because then you execute the code for f,
rather than inlining a copy at the call site.
The new story: InlineRules
~~~~~~~~~~~~~~~~~~~~~~~~~~
The new system removes the InlineMe Note altogether. Instead there
is a new constructor InlineRule in CoreSyn.Unfolding. This is a
bit like a RULE, in that it remembers the template to be inlined inside
the InlineRule. No simplification or inlining is done on an InlineRule,
just like RULEs.
An Id can have an InlineRule *or* a CoreUnfolding (since these are two
constructors from Unfolding). The simplifier treats them differently:
- An InlineRule is has the substitution applied (like RULES) but
is otherwise left undisturbed.
- A CoreUnfolding is updated with the new RHS of the definition,
on each iteration of the simplifier.
An InlineRule fires regardless of size, but *only* when the function
is applied to enough arguments. The "arity" of the rule is specified
(by the programmer) as the number of args on the LHS of the "=". So
it makes a difference whether you say
{-# INLINE f #-}
f x = \y -> e or f x y = e
This is one of the big new features that InlineRule gives us, and it
is one that Roman really wanted.
In contrast, a CoreUnfolding can fire when it is applied to fewer
args than than the function has lambdas, provided the result is small
enough.
Consequential stuff
~~~~~~~~~~~~~~~~~~~
* A 'wrapper' no longer has a WrapperInfo in the IdInfo. Instead,
the InlineRule has a field identifying wrappers.
* Of course, IfaceSyn and interface serialisation changes appropriately.
* Making implication constraints inline nicely was a bit fiddly. In
the end I added a var_inline field to HsBInd.VarBind, which is why
this patch affects the type checker slightly
* I made some changes to the way in which eta expansion happens in
CorePrep, mainly to ensure that *arguments* that become let-bound
are also eta-expanded. I'm still not too happy with the clarity
and robustness fo the result.
* We now complain if the programmer gives an INLINE pragma for
a recursive function (prevsiously we just ignored it). Reason for
change: we don't want an InlineRule on a LoopBreaker, because then
we'd have to check for loop-breaker-hood at occurrence sites (which
isn't currenlty done). Some tests need changing as a result.
This patch has been in my tree for quite a while, so there are
probably some other minor changes.
Diffstat (limited to 'compiler/stranal')
-rw-r--r-- | compiler/stranal/WorkWrap.lhs | 25 | ||||
-rw-r--r-- | compiler/stranal/WwLib.lhs | 4 |
2 files changed, 13 insertions, 16 deletions
diff --git a/compiler/stranal/WorkWrap.lhs b/compiler/stranal/WorkWrap.lhs index 438afd6cf5..6ddbbd8fa4 100644 --- a/compiler/stranal/WorkWrap.lhs +++ b/compiler/stranal/WorkWrap.lhs @@ -16,18 +16,16 @@ module WorkWrap ( wwTopBinds, mkWrapper ) where #include "HsVersions.h" import CoreSyn -import CoreUnfold ( certainlyWillInline ) +import CoreUnfold ( certainlyWillInline, mkWwInlineRule ) import CoreLint ( showPass, endPass ) import CoreUtils ( exprType, exprIsHNF, exprArity ) import Id ( Id, idType, isOneShotLambda, setIdNewStrictness, mkWorkerId, - setIdWorkerInfo, setInlinePragma, - setIdArity, idInfo ) + setInlinePragma, setIdUnfolding, setIdArity, idInfo ) import MkId ( lazyIdKey, lazyIdUnfolding ) import Type ( Type ) -import IdInfo ( WorkerInfo(..), arityInfo, - newDemandInfo, newStrictnessInfo, unfoldingInfo, inlinePragInfo - ) +import IdInfo ( arityInfo, newDemandInfo, newStrictnessInfo, + unfoldingInfo, inlinePragInfo ) import NewDemand ( Demand(..), StrictSig(..), DmdType(..), DmdResult(..), Demands(..), mkTopDmdType, isBotRes, returnsCPR, topSig, isAbsent ) @@ -114,16 +112,12 @@ matching by looking for strict arguments of the correct type. \begin{code} wwExpr :: CoreExpr -> UniqSM CoreExpr -wwExpr e@(Type _) = return e -wwExpr e@(Lit _) = return e -wwExpr e@(Note InlineMe expr) = return e - -- Don't w/w inside InlineMe's - +wwExpr e@(Type _) = return e +wwExpr e@(Lit _) = return e wwExpr e@(Var v) | v `hasKey` lazyIdKey = return lazyIdUnfolding | otherwise = return e -- HACK alert: Inline 'lazy' after strictness analysis - -- (but not inside InlineMe's) wwExpr (Lam binder expr) = Lam binder <$> wwExpr expr @@ -172,7 +166,10 @@ The only reason this is monadised is for the unique supply. Note [Don't w/w inline things] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It's very important to refrain from w/w-ing an INLINE function -If we do so by mistake we transform +because the wrapepr will then overwrite the InlineRule unfolding. + +It was wrong with the old InlineMe Note too: if we do so by mistake +we transform f = __inline (\x -> E) into f = __inline (\x -> case x of (a,b) -> fw E) @@ -268,7 +265,7 @@ splitFun fn_id fn_info wrap_dmds res_info inline_prag rhs -- arity is consistent with the demand type goes through wrap_rhs = wrap_fn work_id - wrap_id = fn_id `setIdWorkerInfo` HasWorker work_id arity + wrap_id = fn_id `setIdUnfolding` mkWwInlineRule wrap_rhs arity work_id ; return ([(work_id, work_rhs), (wrap_id, wrap_rhs)]) }) -- Worker first, because wrapper mentions it diff --git a/compiler/stranal/WwLib.lhs b/compiler/stranal/WwLib.lhs index 0bde744385..43aabc3895 100644 --- a/compiler/stranal/WwLib.lhs +++ b/compiler/stranal/WwLib.lhs @@ -134,8 +134,8 @@ mkWwBodies fun_ty demands res_info one_shots ; let (work_lam_args, work_call_args) = mkWorkerArgs work_args res_ty ; return ([idNewDemandInfo v | v <- work_call_args, isIdVar v], - Note InlineMe . wrap_fn_args . wrap_fn_cpr . wrap_fn_str . applyToVars work_call_args . Var, - mkLams work_lam_args. work_fn_str . work_fn_cpr . work_fn_args) } + wrap_fn_args . wrap_fn_cpr . wrap_fn_str . applyToVars work_call_args . Var, + mkLams work_lam_args. work_fn_str . work_fn_cpr . work_fn_args) } -- We use an INLINE unconditionally, even if the wrapper turns out to be -- something trivial like -- fw = ... |