Skip to content

Constructors and such should be able to move around seq# sometimes

This code

module SeqCon where

import Control.Exception (evaluate)

blah :: Int -> IO Int
blah x = (+2) <$> evaluate (x + 3)

compiled with -O2 -ddump-prep -dsuppress-coercions produces the following (and -ddump-stg doesn't change much of note):

SeqCon.blah1
  :: GHC.Types.Int
     -> GHC.Prim.State# GHC.Prim.RealWorld
     -> (# GHC.Prim.State# GHC.Prim.RealWorld, GHC.Types.Int #)
[GblId,
 Arity=2,
 Caf=NoCafRefs,
 Str=<L,1*U(U)><S,U>,
 Unf=OtherCon []]
SeqCon.blah1
  = \ (x_s1cz [Occ=Once!] :: GHC.Types.Int)
      (s_s1cA [Occ=Once] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
      let {
        sat_s1cE [Occ=Once] :: GHC.Types.Int
        [LclId]
        sat_s1cE
          = case x_s1cz of { GHC.Types.I# x1_s1cC [Occ=Once] ->
            case GHC.Prim.+# x1_s1cC 3# of sat_s1cD { __DEFAULT ->
            GHC.Types.I# sat_s1cD
            }
            } } in
      case GHC.Prim.seq#
             @ GHC.Types.Int @ GHC.Prim.RealWorld sat_s1cE s_s1cA
      of
      { (# ipv_s1cG [Occ=Once], ipv1_s1cH [Occ=Once!] #) ->
      let {
        sat_s1cL [Occ=Once] :: GHC.Types.Int
        [LclId]
        sat_s1cL
          = case ipv1_s1cH of { GHC.Types.I# x1_s1cJ [Occ=Once] ->
            case GHC.Prim.+# x1_s1cJ 2# of sat_s1cK { __DEFAULT ->
            GHC.Types.I# sat_s1cK
            }
            } } in
      (# ipv_s1cG, sat_s1cL #)
      }

-- RHS size: {terms: 5, types: 3, coercions: 5, joins: 0/0}
SeqCon.blah :: GHC.Types.Int -> GHC.Types.IO GHC.Types.Int
[GblId,
 Arity=2,
 Caf=NoCafRefs,
 Str=<L,1*U(U)><S,U>,
 Unf=OtherCon []]
SeqCon.blah
  = (\ (eta_B2 [Occ=Once] :: GHC.Types.Int)
       (eta_B1 [Occ=Once] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
       SeqCon.blah1 eta_B2 eta_B1)
    `cast` <Co:5>

This builds a closure to build an Int and passes it to seq#. That seems a bit wasteful, since we don't actually need the Int box. I think what we'd really like to end up with is something like

blah1 = \ (x :: Int) (s :: State# RealWorld) ->
  case seq# x s of { (# s', x' #) ->
  case x' of { I# x# -> (# s', I# (x# +# 5#) #) }}

Here's one vague idea: when we analyse x+3 (the argument to seq#) under a strict demand, we see it is strict in x. So we can transform seq# (x + 3) s into

case seq# x s of
  (# s', x' #) -> seq# (x' + 3) s'

We know that x' is in WHNF, so we should (I think) be able to see that x' + 3 isn't bottom, so we can use

case seq# x s of {(# s', x' #) ->
case x' + 3 of {!res -> s', res}}

Side note: the redundant eta-expansion in blah is a bit surprising. blah1 already has arity 2, so I'd have expected blah to just coerce it directly.

Trac metadata
Trac field Value
Version 8.2.1-rc2
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