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 |