Skip to content

WorkWrap Idea: Unboxing higher-order function results based on strictness

Consider

{-# OPTIONS_GHC -O2 -fforce-recomp #-}

module Lib where

import System.Environment

f :: (Int -> Int) -> [Int] -> Int
f g [] = 0
f g (x:xs) = g x + f g xs

main = do
  (n:_)<- map read <$> getArgs
  print $ f (*n) [1..1000]

Currently, we get the following w/w split for f:

$wf :: (Int -> Int) -> [Int] -> Int#
$wf g xs = ... case g x of I# n -> n +# ...
f g xs = I# ($wf g xs)

What I want is

$wf :: (Int -> Int#) -> [Int] -> Int#
$wf g' xs = ... case g' x :: Int# of n -> n +# ...
f g xs = I# ($wf (\n -> case g n of I# m -> m) xs)

Thus one fewer box to pass from g to f.

Note that polarity changes whether we can unbox args or results based on strictness. Here we may unbox the result of the higher-order function because we never use its box. Note also that we allocate an additional closure for the new lambda if f doesn't inline, so this might not be ideal. Nevertheless, worth considering. With boxity analysis (#19871 (closed)), we should have all the demand analysis machinery we need to exploit this. E.g., f puts a demand of LCL(!1L) on g, so we see that its result is used unboxed.

It's not unlike WW'ing for arity, where the wrapper has to allocate a closure for the squiggly lambda wrapper around the non-squiggly lambda call to g. Here, we simply extend the idea to unbox the result of that call to g.

Edited by Sebastian Graf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information