Skip to content

NOINLINE and worker/wrapper

Currently we do no worker/wrapper on a NOINLINE thing. In WorkWrap:

tryWW dflags fam_envs is_rec fn_id rhs
  | isNeverActive inline_act
        -- No point in worker/wrappering if the thing is never inlined!
        -- Because the no-inline prag will prevent the wrapper ever
        -- being inlined at a call site.

But if we have, say,

{-# NOINLINE f #-}
f (x,y) = error (show x)

g True  p = f p
g False p = snd p + 1

then strictness analysis will discover f is strict, and g, but because f has no wrapper, the worker for g will rebox the thing. So we get

f (x,y) = error (show x)

$wg b x y = let p = (x,y)  -- Yikes! Reboxing!
            in case b of
              True  -> f p
              False -> y + 1

g b p = case p of (x,y) -> $wg b x y

Now, in this case the reboxing will float into the True branch, an so the allocation will only happen on the error path. But it won't float inwards if there are multiple branches that call (f p), so the reboxing will happen on every call of g. Disaster.

Solution: do worker/wrapper even on NOINLINE things; but move the NOINLINE pragma to the worker.


This actually happens! In GHC.Arr we have

{-# NOINLINE indexError #-}
indexError :: Show a => (a,a) -> a -> String -> b
indexError rng i tp = error (...)

index b i | inRange b i =  unsafeIndex b i
          | otherwise   =  indexError b i "Char"

The inRange generates multiple alternatives, which the indexError is duplicated into, and exactly this phenomenon takes place. Eric (gridaphobe) offered this standalone example

module Err where

tabulate :: (Int -> a) -> (Int, Int) -> [Int]
tabulate f (l,u) = array (l,u) [l..u]

{-# INLINE array #-}
array :: (Int, Int) -> [Int] -> [Int]
array (l,u) is = [index (l,u) i | i <- is]

{-# INLINE index #-}
index :: (Int, Int) -> Int -> Int
index b@(l,h) i
  | l <= i && i < h = 0
  | otherwise       = indexError b i 0

{-# NOINLINE indexError #-}
indexError :: (Int, Int) -> Int -> Int -> b
indexError rng i tp = error (show rng)

Compile this with GHC 8, and shudder at the terrible code we get for $wtabulate.

Trac metadata
Trac field Value
Version 8.0.1
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information