Consider eta-expanding functions during Worker/Wrapper when it allows for more W/W splits to happen.
Given code like below we would like to see a worker being produced for both g
and f
.
{-# NOINLINE g #-}
g :: Int -> Int
g = f
{-# NOINLINE f #-}
f :: Int -> Int
f !x = x + 1
However currently only f
get's a worker:
-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
Lib.$wf [InlPrag=NOINLINE] :: GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=1, Str=<L>, Unf=OtherCon []]
Lib.$wf = \ (ww_sFI :: GHC.Prim.Int#) -> GHC.Prim.+# ww_sFI 1#
-- RHS size: {terms: 10, types: 4, coercions: 0, joins: 0/0}
f [InlPrag=[final]] :: Int -> Int
[GblId,
Arity=1,
Str=<1!P(L)>,
Cpr=1,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_sFG [Occ=Once1!] :: Int) ->
case x_sFG of { I# ww_sFI [Occ=Once1] ->
case Lib.$wf ww_sFI of ww1_sFN [Occ=Once1] { __DEFAULT ->
GHC.Types.I# ww1_sFN
}
}}]
f = \ (x_sFG :: Int) ->
case x_sFG of { I# ww_sFI ->
case Lib.$wf ww_sFI of ww1_sFN { __DEFAULT ->
GHC.Types.I# ww1_sFN
}
}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
g [InlPrag=NOINLINE] :: Int -> Int
[GblId, Arity=1, Str=<1!P(L)>, Cpr=1, Unf=OtherCon []]
g = f
@sgraf812 suggests we should generate a worker for g
as well, and do this by doing eta-expansion on bindings like f
in the body of g
if we know they are W/Wed.
So perhaps ... we should eta expand
g
in WW iff
is known to be worker/wrappered. Then we'll also get a worker$wg
that will inherit the NOINLINE pragma and we can callg
efficiently.
This of course, might cause us to run into #20364 more often. Which in turn would require more frequent use of either INLINEABLE or OPAQUE by users to avoid. So it's not clear it's a win
For what it's worth on HEAD simply rewriting g = f
into g !x = f
causes a worker wrapper split for g
and that seems like a more direct way of ensuring W/W happens. So I'm currently less convinced of this idea than I was when I first read it.