Inadequate FloatIn pass may leave redundant allocations in the hot path
Consider
module T21128a where
indexError :: Show a => a -> a -> a -> b
indexError a b c = error (show a ++ show b ++ show c)
{-# NOINLINE indexError #-}
index :: Int -> Int -> Int -> Int
index l u i
| l <= i && i < u = i-l
| otherwise = indexError l u i
{-# INLINE index #-}
module T21128 where
import T21128a
theresCrud :: Int -> Int -> Int
theresCrud x y = go x
where
go 0 = index 0 y 0
go 1 = index x y 1
go n = go (n-1)
{-# NOINLINE theresCrud #-}
The GHC versions I've looked at (8.10 and master) will unbox x
and y
. But that will stop with the following simplified Core (output here from master):
lvl = \ y -> $windexError $fShowInt_$cshow l y l
lvl1 = \ ww y -> $windexError $fShowInt_$cshow (I# ww) y i
$wtheresCrud
= \ ww ww1 ->
let { y = I# ww1 } in
join {
lvl2
= case <=# ww 1# of {
__DEFAULT -> case lvl1 ww y of wild { };
1# ->
case <# 1# ww1 of {
__DEFAULT -> case lvl1 ww y of wild { };
1# -> -# 1# ww
}
} } in
join {
lvl3
= case <# 0# ww1 of {
__DEFAULT -> case lvl y of wild { };
1# -> 0#
} } in
joinrec {
$wgo ww2
= case ww2 of wild {
__DEFAULT -> jump $wgo (-# wild 1#);
0# -> jump lvl3;
1# -> jump lvl2
}; } in
jump $wgo ww
NB: Those exit join points correspond to the two index
calls.
Note the reboxing of y
. While the reboxing of x
happens in the bottoming join point exit_X18
, y
is shared among both index
calls and can't be floated into the exit join points without duplicating the binding. Similarly, y
won't be inlined, because ... I don't know why. Code size concerns maybe.
The result is that y
is allocated in the hot path where it is never needed boxed.
This issue is merely to document this inconvenience. Not sure if we want to take concrete action on such a narrow use case; probably depends on how involved the fix is. Something for @simonpj to ponder.