Bad choice of loop breaker with INLINABLE/INLINE
Take the following program, which defines a pair of lists recursively:
module Test(xs, ys) where
pair :: ([Bool], [Bool])
pair = (False:xs, True:ys)
where
(xs, ys) = pair
(xs, ys) = pair
GHC is clever enough to disentangle xs
from ys
and with -ddump-simpl -O
I get:
Rec {
xs [Occ=LoopBreaker] :: [Bool]
[GblId, Caf=NoCafRefs, Str=DmdType]
xs = : False xs
end Rec }
Rec {
ys [Occ=LoopBreaker] :: [Bool]
[GblId, Caf=NoCafRefs, Str=DmdType]
ys = : True ys
end Rec }
However, if I mark pair
as INLINABLE
or INLINE
(it doesn't matter which), I get much worse code where xs
and ys
go through pair
:
Rec {
pair [InlPrag=INLINABLE[ALWAYS], Occ=LoopBreaker]
:: ([Bool], [Bool])
[GblId,
Str=DmdType m,
Unf=Unf{Src=InlineStable, TopLvl=True, Arity=0, Value=True,
ConLike=True, WorkFree=False, Expandable=True,
Guidance=IF_ARGS [] 50 30
Tmpl= (: False
(case pair of _ { (xs1_Xf6 [Occ=Once], _) -> xs1_Xf6 }),
: True (case pair of _ { (_, ys1_Xf6 [Occ=Once]) -> ys1_Xf6 }))}]
pair = (a1_rgo, a_rgn)
ys_ys :: [Bool]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
ConLike=False, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 0}]
ys_ys = case pair of _ { (xs1_XeW, ys1_Xf7) -> ys1_Xf7 }
a_rgn :: [Bool]
[GblId, Str=DmdType]
a_rgn = : True ys_ys
xs_xs :: [Bool]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
ConLike=False, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 0}]
xs_xs = case pair of _ { (xs1_XeW, ys1_XeS) -> xs1_XeW }
a1_rgo :: [Bool]
[GblId, Str=DmdType]
a1_rgo = : False xs_xs
end Rec }
ys :: [Bool]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
ConLike=False, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=True)}]
ys = ys_ys
xs :: [Bool]
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
ConLike=False, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=True)}]
xs = xs_xs
It seems that GHC chooses pair
as the loop breaker, which stops the simplifier in its tracks.
It might seem a bit silly to mark pair
as INLINE
, since it's not mutually recursive. The function I really had was polymorphic with a typeclass constraint, and I wrote INLINABLE
to get it specialised at its call site. (Also, pair
is mutually recursive in the Core, so you would expect GHC to avoid using it as a loop breaker if I mark it INLINE
.)
Trac metadata
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |