Skip to content

Cast worker/wrapper does not respect -fno-worker-wrapper

Summary

The cast worker/wrapper transformation does not respect the -fno-worker-wrapper flag. I discovered this when 6d49d5be started replacing the NOINLINE annotation on the wrapper with NOUSERINLINE. This is a problem for the Clash Haskell-to-Hardware compiler because we mark/it recognizes certain functions as "primitives", and those functions should not be inlined by GHC.

Steps to reproduce

Compiling the following with ghc -O0 -fno-worker-wrapper Test9.hs

{-# LANGUAGE TypeOperators, DataKinds, KindSignatures #-}
{-# OPTIONS_GHC -O0 -fforce-recomp -fno-worker-wrapper -dverbose-core2core -ddump-ds -ddump-simpl -ddump-to-file #-}
module Test9 where

import GHC.TypeLits

newtype Signed (n :: Nat) = S Integer

{-# NOINLINE times #-}
times :: Signed m -> Signed n -> Signed (m + n)
times (S a) (S b) = S (a * b)

shows this desugar output:

-- RHS size: {terms: 8, types: 7, coercions: 9, joins: 0/0}
times [InlPrag=NOINLINE]
  :: forall (m :: Nat) (n :: Nat).
     Signed m -> Signed n -> Signed (m + n)
[LclIdX,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0] 40 0}]
times
  = \ (@(m_aBd :: Nat))
      (@(n_aBe :: Nat))
      (ds_dBO :: Signed m_aBd)
      (ds_dBP :: Signed n_aBe) ->
      (* @Integer
         GHC.Num.$fNumInteger
         (ds_dBO
          `cast` (Test9.N:Signed[0] <m_aBd>_P :: Signed m_aBd ~R# Integer))
         (ds_dBP
          `cast` (Test9.N:Signed[0] <n_aBe>_P :: Signed n_aBe ~R# Integer)))
      `cast` (Sym (Test9.N:Signed[0] <m_aBd + n_aBe>_P)
              :: Integer ~R# Signed (m_aBd + n_aBe))

but shows this for the simplifier output:

-- RHS size: {terms: 8, types: 7, coercions: 4, joins: 0/0}
times1_rBI
  :: forall {m :: Nat} {n :: Nat}. Signed m -> Signed n -> Integer
[GblId, Arity=2, Unf=OtherCon []]
times1_rBI
  = \ (@(m_aBe :: Nat))
      (@(n_aBf :: Nat))
      (ds_dBP :: Signed m_aBe)
      (ds1_dBQ :: Signed n_aBf) ->
      * @Integer
        GHC.Num.$fNumInteger
        (ds_dBP
         `cast` (Test9.N:Signed[0] <m_aBe>_P :: Signed m_aBe ~R# Integer))
        (ds1_dBQ
         `cast` (Test9.N:Signed[0] <n_aBf>_P :: Signed n_aBf ~R# Integer))

-- RHS size: {terms: 1, types: 0, coercions: 17, joins: 0/0}
times [InlPrag=NOUSERINLINE[final]]
  :: forall (m :: Nat) (n :: Nat).
     Signed m -> Signed n -> Signed (m + n)
[GblId, Arity=2, Unf=OtherCon []]
times
  = times1_rBI
    `cast` (forall (m :: <Nat>_N) (n :: <Nat>_N).
            <Signed m>_R
            %<'Many>_N ->_R <Signed n>_R
            %<'Many>_N ->_R Sym (Test9.N:Signed[0] <m + n>_P)
            :: (forall {m :: Nat} {n :: Nat}. Signed m -> Signed n -> Integer)
               ~R# (forall {m :: Nat} {n :: Nat}.
                    Signed m -> Signed n -> Signed (m + n)))

Expected behavior

I am expecting the cast W/W transformation not to occur and keep a single binding called times having a NOINLINE pragma. Basically, I expected an additional condition in https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Core/Opt/Simplify.hs#L525-534 that checks whether W/W is disabled through -fno-worker-wrapper.

Environment

  • GHC version used: 9.0.1

Optional:

  • Operating System: Linux 4.19.84-microsoft-standard #1 SMP Wed Nov 13 11:44:37 UTC 2019 x86_64 GNU/Linux
  • System Architecture: x86_64
Edited by Christiaan Baaij
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information