Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
GHC
GHC
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,251
    • Issues 4,251
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
    • Iterations
  • Merge Requests 398
    • Merge Requests 398
  • Requirements
    • Requirements
    • List
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
  • Security & Compliance
    • Security & Compliance
    • Dependency List
    • License Compliance
  • Operations
    • Operations
    • Incidents
    • Environments
  • Analytics
    • Analytics
    • CI / CD
    • Code Review
    • Insights
    • Issue
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Glasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #17673

Closed
Open
Opened Jan 13, 2020 by Sebastian Graf@sgraf812Developer

Cast WW of a NOINLINE function should mark the worker as NOINLINE, not the wrapper

Consider

module Lib where

import Control.Monad.ST

f :: IO ()
f = return ()
{-# NOINLINE f #-}

g :: ST s ()
g = return ()
{-# NOINLINE g #-}

Compiled with -O2, we get

Lib.g1
  :: forall s. GHC.Prim.State# s -> (# GHC.Prim.State# s, () #)
Lib.g1
  = \ (@ s_a1oA) (s1_X1qD :: GHC.Prim.State# s_a1oA) ->
      (# s1_X1qD, GHC.Tuple.() #)

g [InlPrag=NOINLINE] :: forall s. ST s ()
g = Lib.g1
    `cast` (forall (s :: <*>_N). Sym (GHC.ST.N:ST[0] <s>_N <()>_R)
            :: (forall s. GHC.ST.STRep s ()) ~R# (forall s. ST s ()))

Lib.f1
  :: GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.f1
  = \ (s_a1r4 :: GHC.Prim.State# RealWorld) ->
      (# s_a1r4, GHC.Tuple.() #)

f [InlPrag=NOINLINE] :: IO ()
f = Lib.f1
    `cast` (Sym (GHC.Types.N:IO[0] <()>_R)
            :: (GHC.Prim.State# RealWorld
                -> (# GHC.Prim.State# RealWorld, () #))
               ~R# IO ())

Note that f and g (the wrappers) are marked NOINLINE instead of Lib.f1 and Lib.g1 (the workers wrt. casts/eta expansion), respectively. This might not look harmful in itself, but it inhibits NOINLINE function in IO and ST from exposing wrappers as a result from strictness analysis (which is useful, #13143 (closed)), as the following example demonstrates:

module Lib where

h :: Int -> IO ()
h x = do
  x `seq` return ()
  print "foo"
  print "foo"
  print "foo"
{-# NOINLINE h #-}
Lib.$wh
  :: Int
     -> GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.$wh = <elided>

Lib.h1
  :: Int
     -> GHC.Prim.State# RealWorld -> (# GHC.Prim.State# RealWorld, () #)
Lib.h1
  = \ (w_s1MP :: Int) (w1_s1MQ :: GHC.Prim.State# RealWorld) ->
      case w_s1MP of w2_X1Ni { GHC.Types.I# ipv_s1MX ->
      Lib.$wh w2_X1Ni w1_s1MQ
      }

h [InlPrag=NOINLINE] :: Int -> IO ()
h = Lib.h1
    `cast` (<Int>_R ->_R Sym (GHC.Types.N:IO[0] <()>_R)
            :: (Int
                -> GHC.Prim.State# RealWorld
                -> (# GHC.Prim.State# RealWorld, () #))
               ~R# (Int -> IO ()))

in #13143 (closed) we decided to mark the worker as NOINLINE, so we should do the same for eta expansion.

Edited May 26, 2020 by Sebastian Graf
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None
Reference: ghc/ghc#17673