Floating unboxed tuple expressions to top-level
Consider this program:
{-# LANGUAGE MagicHash, UnboxedTuples #-}
module Lib where
blah :: Int -> (# Int, [Int] #)
blah x = (# x, replicate x x #)
{-# NOINLINE blah #-}
foo :: Int -> Int -> Int
foo x =
let go [] = (* x)
go (x:xs) = go xs . (+ x)
z = case blah 42 of (# x, xs #) -> go (x:xs)
in z
Ideally, we'd float out the blah 42
expression to top-level and eta-expand z
. Indeed that's what we do if blah
returns a lifted pair, but here blah
returns an unboxed (and thus unlifted) pair. Thus, GHC hesitates to float unlifted stuff (to top-level at least, #17521) and we get bad code.
This comes up at the moment whenever we inline the wrapper of a CPR'd function and didn't manage to float out the wrapper call before (reasons unclear). One solution is to defer inlining of the wrapper, which already is the case (wrappers are inlined in the final phase). But for any pass that happens after final phase Simplification, we will see these function calls with unlifted result type which we are unable to float out (in the FloatOut phase that follows DmdAnal, perhaps). Besides, it's complicated to think about these phase orderings.
Thus I propose the following transformation, at least for things that can float to top-level:
{-# LANGUAGE MagicHash, UnboxedTuples #-}
module Lib where
blah :: Int -> (# Int, [Int] #)
blah x = (# x, replicate x x #)
{-# NOINLINE blah #-}
lvl :: (Int, [Int])
lvl = case blah 42 of (# x, xs #) -> (x, xs)
foo :: Int -> Int -> Int
foo x =
let go [] = (* x)
go (x:xs) = go xs . (+ x)
z = case lvl of (x, xs) -> go (x:xs)
in z
and then eta-expand z
, as expected. Not so sure if this is a big win if the thing can't float to top-level, though. Perhaps it still is if it enables further eta-expansion.