Cheng Shao pushed to branch wip/deepseq-primop at Glasgow Haskell Compiler / GHC

Commits:

16 changed files:

Changes:

  • compiler/GHC/Builtin/primops.txt.pp
    ... ... @@ -4537,6 +4537,23 @@ primop PrefetchValueOp0 "prefetchValue0#" GenPrimOp
    4537 4537
        with effect = ReadWriteEffect
    
    4538 4538
     
    
    4539 4539
     
    
    4540
    +------------------------------------------------------------------------
    
    4541
    +section "Forcing evaluation"
    
    4542
    +        {Primitives for forcing evaluation within a state thread.}
    
    4543
    +------------------------------------------------------------------------
    
    4544
    +
    
    4545
    +primop DeepSeqOp "deepseq#" GenPrimOp
    
    4546
    +   a -> State# s -> (# State# s, Int#, a #)
    
    4547
    +   { @'deepseq#' x s@ deeply evaluates @x@ in the state thread; see
    
    4548
    +     'GHC.DeepSeq.forceIO' for the user-facing semantics. }
    
    4549
    +   with
    
    4550
    +   out_of_line = True
    
    4551
    +   effect = ReadWriteEffect
    
    4552
    +   -- See Note [seq# magic] in GHC.Types.Id.Make: we must not let strictness
    
    4553
    +   -- analysis see through the sequencing effect.
    
    4554
    +   strictness = { \ _arity -> mkClosedDmdSig [ topDmd, topDmd ] topDiv }
    
    4555
    +
    
    4556
    +
    
    4540 4557
     -- Note [RuntimeRep polymorphism in continuation-style primops]
    
    4541 4558
     -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    4542 4559
     --  See below.
    

  • compiler/GHC/StgToCmm/Prim.hs
    ... ... @@ -1705,6 +1705,7 @@ emitPrimOp cfg primop =
    1705 1705
       AtomicModifyMutVar2Op -> alwaysExternal
    
    1706 1706
       AtomicModifyMutVar_Op -> alwaysExternal
    
    1707 1707
       CasMutVarOp -> alwaysExternal
    
    1708
    +  DeepSeqOp -> alwaysExternal
    
    1708 1709
       CatchOp -> alwaysExternal
    
    1709 1710
       RaiseOp -> alwaysExternal
    
    1710 1711
       RaiseUnderflowOp -> alwaysExternal
    

  • compiler/GHC/StgToJS/Prim.hs
    ... ... @@ -1155,6 +1155,8 @@ genPrim prof bound ty op = case op of
    1155 1155
     
    
    1156 1156
     ------------------------------ Unhandled primops -------------------
    
    1157 1157
     
    
    1158
    +  DeepSeqOp                         -> unhandledPrimop op
    
    1159
    +
    
    1158 1160
       AnnotateStackOp                   -> unhandledPrimop op
    
    1159 1161
     
    
    1160 1162
       NewPromptTagOp                    -> unhandledPrimop op
    

  • libraries/base/src/GHC/Base.hs
    ... ... @@ -149,6 +149,9 @@ import GHC.Prim hiding
    149 149
       -- whereFrom# is similarly internal.
    
    150 150
       , whereFrom#
    
    151 151
       , isByteArrayWeaklyPinned#, isMutableByteArrayWeaklyPinned#
    
    152
    +  -- Users should use the interface provided by GHC.DeepSeq in
    
    153
    +  -- ghc-experimental.
    
    154
    +  , deepseq#
    
    152 155
       -- Don't re-export vector FMA instructions
    
    153 156
       , fmaddFloatX4#
    
    154 157
       , fmsubFloatX4#
    

  • libraries/base/src/GHC/Exts.hs
    ... ... @@ -124,6 +124,10 @@ import GHC.Prim hiding
    124 124
       , whereFrom#
    
    125 125
       , isByteArrayWeaklyPinned#, isMutableByteArrayWeaklyPinned#
    
    126 126
     
    
    127
    +  -- Users should use the interface provided by GHC.DeepSeq in
    
    128
    +  -- ghc-experimental.
    
    129
    +  , deepseq#
    
    130
    +
    
    127 131
       -- Don't re-export vector FMA instructions
    
    128 132
       , fmaddFloatX4#
    
    129 133
       , fmsubFloatX4#
    

  • libraries/ghc-experimental/ghc-experimental.cabal.in
    ... ... @@ -31,6 +31,7 @@ library
    31 31
         exposed-modules:
    
    32 32
           Data.Sum.Experimental
    
    33 33
           Data.Tuple.Experimental
    
    34
    +      GHC.DeepSeq
    
    34 35
           GHC.PrimOps
    
    35 36
           GHC.Profiling.Eras
    
    36 37
           GHC.TypeLits.Experimental
    

  • libraries/ghc-experimental/src/GHC/DeepSeq.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +{-# LANGUAGE UnboxedTuples #-}
    
    3
    +
    
    4
    +module GHC.DeepSeq
    
    5
    +  ( force,
    
    6
    +    forceIO,
    
    7
    +  )
    
    8
    +where
    
    9
    +
    
    10
    +import GHC.IO
    
    11
    +import GHC.Internal.Exts
    
    12
    +
    
    13
    +-- | Pure wrapper around 'forceIO'.
    
    14
    +force :: a -> (Bool, a)
    
    15
    +force = unsafePerformIO . forceIO
    
    16
    +
    
    17
    +-- | Deeply evaluate a value in the 'IO' monad, returning the forced value and
    
    18
    +-- a flag indicating whether any unevaluated closure was forced.
    
    19
    +--
    
    20
    +-- This is a primitive analogue of 'Control.DeepSeq.force' / @rnf@ that does
    
    21
    +-- not require an 'NFData' constraint. It traverses algebraic data (constructor
    
    22
    +-- fields), immutable arrays, and the contents of 'MutVar#', 'MVar#',
    
    23
    +-- 'MutableArray#', 'SmallMutableArray#', and live 'Weak#' values.
    
    24
    +--
    
    25
    +-- To mimic typical 'Control.DeepSeq.NFData' instances, it stops at
    
    26
    +-- function-like closures (e.g. functions and partial applications) and at
    
    27
    +-- mutable objects which are not plain containers (e.g. 'TVar#'). Consequently
    
    28
    +-- it is not a drop-in replacement for user-defined 'NFData' instances, which
    
    29
    +-- may choose to force less (or more) depending on semantics.
    
    30
    +--
    
    31
    +-- === Pointer traversal policy
    
    32
    +--
    
    33
    +-- We only follow a pointer when doing so is also possible in Haskell via a
    
    34
    +-- corresponding API. For example, we traverse 'MutVar#', 'MVar#', mutable
    
    35
    +-- arrays, and live weak pointers because you can observe their contents with
    
    36
    +-- operations like @readIORef@, @readMVar@, @readArray@, or @deRefWeak@.
    
    37
    +-- Conversely, we do not peek inside closures whose internals are not
    
    38
    +-- observable from Haskell, such as function closures and their captured free
    
    39
    +-- variables.
    
    40
    +--
    
    41
    +-- Like any deep evaluation, it may not terminate on cyclic structures.
    
    42
    +forceIO :: a -> IO (Bool, a)
    
    43
    +forceIO a = IO $ \s0 -> case deepseq# a s0 of
    
    44
    +  (# s1, flag#, a' #) -> (# s1, (isTrue# flag#, a') #)

  • rts/DeepSeq.cmm
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team, 2025
    
    4
    + *
    
    5
    + * Support for the deepseq# primop.
    
    6
    + *
    
    7
    + * ---------------------------------------------------------------------------*/
    
    8
    +
    
    9
    +#include "Cmm.h"
    
    10
    +
    
    11
    +/*
    
    12
    +Note [import CLOSURE annotations]
    
    13
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    14
    +See Note [import CLOSURE annotations] in rts/Apply.cmm.
    
    15
    +*/
    
    16
    +
    
    17
    +#if !defined(UnregisterisedCompiler)
    
    18
    +import CLOSURE g0;
    
    19
    +import CLOSURE large_alloc_lim;
    
    20
    +#endif
    
    21
    +
    
    22
    +/* -----------------------------------------------------------------------------
    
    23
    +   deepseq#
    
    24
    +
    
    25
    +   Deeply evaluate a value to (approximate) normal form, without requiring an
    
    26
    +   NFData constraint. This is used to provide a primitive analogue of
    
    27
    +   Control.DeepSeq.force / rnf.
    
    28
    +
    
    29
    +   See the primop documentation in compiler/GHC/Builtin/primops.txt.pp for the
    
    30
    +   intended semantics and limitations.
    
    31
    +   -------------------------------------------------------------------------- */
    
    32
    +
    
    33
    +// Worker which performs deep evaluation. This lets us tail-call when traversing
    
    34
    +// the final pointer field, avoiding stack blowup on common spine-recursive
    
    35
    +// structures (e.g. lists).
    
    36
    +//
    
    37
    +// The second argument is a boolean (0/1) accumulator tracking whether any
    
    38
    +// evaluation was forced in the transitive closure so far.
    
    39
    +stg_deepseqWorkzh (P_ p, W_ forced)
    
    40
    +{
    
    41
    +    W_ type, info;
    
    42
    +
    
    43
    +    again: MAYBE_GC(again);
    
    44
    +    STK_CHK_GEN();
    
    45
    +
    
    46
    +    p = UNTAG(p);
    
    47
    +    info  = %INFO_PTR(p);
    
    48
    +    type = TO_W_(%INFO_TYPE(%STD_INFO(info)));
    
    49
    +
    
    50
    +    switch [0 .. N_CLOSURE_TYPES] type {
    
    51
    +
    
    52
    +    // Unevaluated things must be evaluated first:
    
    53
    +    case
    
    54
    +        THUNK,
    
    55
    +        THUNK_1_0,
    
    56
    +        THUNK_0_1,
    
    57
    +        THUNK_2_0,
    
    58
    +        THUNK_1_1,
    
    59
    +        THUNK_0_2,
    
    60
    +        THUNK_STATIC,
    
    61
    +        AP,
    
    62
    +        AP_STACK,
    
    63
    +        BLACKHOLE,
    
    64
    +        THUNK_SELECTOR : {
    
    65
    +        (P_ evald) = call %ENTRY_CODE(info) (p);
    
    66
    +        jump stg_deepseqWorkzh(evald, 1);
    
    67
    +    }
    
    68
    +
    
    69
    +    // Follow indirections:
    
    70
    +    case IND, IND_STATIC: {
    
    71
    +        p = %acquire StgInd_indirectee(p);
    
    72
    +        jump stg_deepseqWorkzh(p, forced);
    
    73
    +    }
    
    74
    +
    
    75
    +    // WHITEHOLEs are transient. Yield and try again.
    
    76
    +    case WHITEHOLE: {
    
    77
    +        goto again;
    
    78
    +    }
    
    79
    +
    
    80
    +    // Arrays of pointers: evaluate elements.
    
    81
    +    case
    
    82
    +        MUT_ARR_PTRS_DIRTY,
    
    83
    +        MUT_ARR_PTRS_CLEAN,
    
    84
    +        MUT_ARR_PTRS_FROZEN_DIRTY,
    
    85
    +        MUT_ARR_PTRS_FROZEN_CLEAN: {
    
    86
    +        W_ i_arr, ptrs_arr;
    
    87
    +        ptrs_arr = StgMutArrPtrs_ptrs(p);
    
    88
    +        if (ptrs_arr == 0) { return (forced); }
    
    89
    +        i_arr = ptrs_arr - 1;
    
    90
    +      deepseq_arr_loop0:
    
    91
    +        if (i_arr == 0) ( likely: False ) {
    
    92
    +            // Tail-call the final element to avoid building up a deep stack
    
    93
    +            // when traversing large immutable arrays.
    
    94
    +            jump stg_deepseqWorkzh(P_[p + SIZEOF_StgMutArrPtrs], forced);
    
    95
    +        }
    
    96
    +        (W_ forced_arr) = call stg_deepseqWorkzh(P_[p + SIZEOF_StgMutArrPtrs + WDS(i_arr)], forced);
    
    97
    +        forced = forced_arr;
    
    98
    +        i_arr = i_arr - 1;
    
    99
    +        goto deepseq_arr_loop0;
    
    100
    +    }
    
    101
    +
    
    102
    +    case
    
    103
    +        SMALL_MUT_ARR_PTRS_DIRTY,
    
    104
    +        SMALL_MUT_ARR_PTRS_CLEAN,
    
    105
    +        SMALL_MUT_ARR_PTRS_FROZEN_DIRTY,
    
    106
    +        SMALL_MUT_ARR_PTRS_FROZEN_CLEAN: {
    
    107
    +        W_ i_sarr, ptrs_sarr;
    
    108
    +        ptrs_sarr = StgSmallMutArrPtrs_ptrs(p);
    
    109
    +        if (ptrs_sarr == 0) { return (forced); }
    
    110
    +        i_sarr = ptrs_sarr - 1;
    
    111
    +      deepseq_arr_loop1:
    
    112
    +        if (i_sarr == 0) ( likely: False ) {
    
    113
    +            // Tail-call the final element to avoid building up a deep stack
    
    114
    +            // when traversing large immutable arrays.
    
    115
    +            jump stg_deepseqWorkzh(P_[p + SIZEOF_StgSmallMutArrPtrs], forced);
    
    116
    +        }
    
    117
    +        (W_ forced_sarr) = call stg_deepseqWorkzh(P_[p + SIZEOF_StgSmallMutArrPtrs + WDS(i_sarr)], forced);
    
    118
    +        forced = forced_sarr;
    
    119
    +        i_sarr = i_sarr - 1;
    
    120
    +        goto deepseq_arr_loop1;
    
    121
    +    }
    
    122
    +
    
    123
    +    // Constructors: evaluate their pointer fields.
    
    124
    +    case
    
    125
    +        CONSTR,
    
    126
    +        CONSTR_1_0,
    
    127
    +        CONSTR_0_1,
    
    128
    +        CONSTR_2_0,
    
    129
    +        CONSTR_1_1,
    
    130
    +        CONSTR_0_2,
    
    131
    +        CONSTR_NOCAF: {
    
    132
    +        W_ i_constr, ptrs_constr;
    
    133
    +        ptrs_constr  = TO_W_(%INFO_PTRS(%STD_INFO(info)));
    
    134
    +        if (ptrs_constr == 0) { return (forced); }
    
    135
    +        i_constr = 0;
    
    136
    +      deepseq_constr_loop:
    
    137
    +        if (i_constr < ptrs_constr) {
    
    138
    +            // Tail-call the last one. This avoids building up a deep stack
    
    139
    +            // when traversing long lists. We count up so the final pointer
    
    140
    +            // field (e.g. the tail of a list cell) is tail-called.
    
    141
    +            if (i_constr == ptrs_constr - 1) {
    
    142
    +                jump stg_deepseqWorkzh(StgClosure_payload(p,i_constr), forced);
    
    143
    +            }
    
    144
    +            (W_ forced_constr) = call stg_deepseqWorkzh(StgClosure_payload(p,i_constr), forced);
    
    145
    +            forced = forced_constr;
    
    146
    +            i_constr = i_constr + 1;
    
    147
    +            goto deepseq_constr_loop;
    
    148
    +        }
    
    149
    +        return (forced);
    
    150
    +    }
    
    151
    +
    
    152
    +    case
    
    153
    +        MUT_VAR_CLEAN,
    
    154
    +        MUT_VAR_DIRTY: {
    
    155
    +        p = %relaxed StgMutVar_var(p);
    
    156
    +        jump stg_deepseqWorkzh(p, forced);
    
    157
    +    }
    
    158
    +
    
    159
    +    case
    
    160
    +        MVAR_CLEAN,
    
    161
    +        MVAR_DIRTY: {
    
    162
    +        p = %relaxed StgMVar_value(p);
    
    163
    +        jump stg_deepseqWorkzh(p, forced);
    
    164
    +    }
    
    165
    +
    
    166
    +    case WEAK: {
    
    167
    +        // Follow the value of a live weak pointer.
    
    168
    +        jump stg_deepseqWorkzh(StgWeak_value(p), forced);
    
    169
    +    }
    
    170
    +
    
    171
    +    // Anything else: conservatively stop.
    
    172
    +    //
    
    173
    +    // This includes (among other closure types) function-like closures and
    
    174
    +    // mutable objects which are not plain containers (e.g. TVar#),
    
    175
    +    // matching the intended "mimic typical NFData instances" semantics
    
    176
    +    // described in the primop documentation.
    
    177
    +    //
    
    178
    +    // We should never see frames here, but if we do, returning is safer than
    
    179
    +    // entering arbitrary things.
    
    180
    +    default: {
    
    181
    +        return (forced);
    
    182
    +    }}
    
    183
    +}
    
    184
    +
    
    185
    +// deepseq# primop entry point.
    
    186
    +//   deepseq# :: forall a s. a -> State# s -> (# State# s, Int#, a #)
    
    187
    +//
    
    188
    +// The State# argument/result has no runtime representation, so the RTS entry
    
    189
    +// only takes the value being forced.
    
    190
    +stg_deepseqzh (P_ p)
    
    191
    +{
    
    192
    +    jump stg_deepseqLoopzh(p, 0);
    
    193
    +}
    
    194
    +
    
    195
    +// Worker which evaluates to a root and then delegates to the deep traversal.
    
    196
    +// The second argument is a boolean (0/1) accumulator tracking whether any
    
    197
    +// evaluation was forced in the transitive closure so far.
    
    198
    +stg_deepseqLoopzh (P_ p, W_ forced)
    
    199
    +{
    
    200
    +    W_ type, info, tag;
    
    201
    +
    
    202
    +    again: MAYBE_GC(again);
    
    203
    +    STK_CHK_GEN();
    
    204
    +
    
    205
    +    tag = GETTAG(p);
    
    206
    +    p = UNTAG(p);
    
    207
    +    info  = %INFO_PTR(p);
    
    208
    +    type = TO_W_(%INFO_TYPE(%STD_INFO(info)));
    
    209
    +
    
    210
    +    switch [0 .. N_CLOSURE_TYPES] type {
    
    211
    +
    
    212
    +    // Unevaluated things must be evaluated first:
    
    213
    +    case
    
    214
    +        THUNK,
    
    215
    +        THUNK_1_0,
    
    216
    +        THUNK_0_1,
    
    217
    +        THUNK_2_0,
    
    218
    +        THUNK_1_1,
    
    219
    +        THUNK_0_2,
    
    220
    +        THUNK_STATIC,
    
    221
    +        AP,
    
    222
    +        AP_STACK,
    
    223
    +        BLACKHOLE,
    
    224
    +        THUNK_SELECTOR : {
    
    225
    +        (P_ evald) = call %ENTRY_CODE(info) (p);
    
    226
    +        jump stg_deepseqLoopzh(evald, 1);
    
    227
    +    }
    
    228
    +
    
    229
    +    // Follow indirections:
    
    230
    +    case IND, IND_STATIC: {
    
    231
    +        p = %acquire StgInd_indirectee(p);
    
    232
    +        jump stg_deepseqLoopzh(p, forced);
    
    233
    +    }
    
    234
    +
    
    235
    +    // WHITEHOLEs are transient. Yield and try again.
    
    236
    +    case WHITEHOLE: {
    
    237
    +        goto again;
    
    238
    +    }
    
    239
    +
    
    240
    +    default: {
    
    241
    +        P_ root;
    
    242
    +        root = tag | p;
    
    243
    +        (W_ forced1) = call stg_deepseqWorkzh(root, forced);
    
    244
    +        return (forced1, root);
    
    245
    +    }}
    
    246
    +}

  • rts/RtsSymbols.c
    ... ... @@ -644,6 +644,7 @@ extern char **environ;
    644 644
           SymI_HasDataProto(stg_newMutVarzh)                                    \
    
    645 645
           SymI_HasDataProto(stg_newTVarzh)                                      \
    
    646 646
           SymI_HasDataProto(stg_noDuplicatezh)                                  \
    
    647
    +      SymI_HasDataProto(stg_deepseqzh)                                      \
    
    647 648
           SymI_HasDataProto(stg_atomicModifyMutVar2zh)                          \
    
    648 649
           SymI_HasDataProto(stg_atomicModifyMutVarzuzh)                         \
    
    649 650
           SymI_HasDataProto(stg_casMutVarzh)                                    \
    

  • rts/include/stg/MiscClosures.h
    ... ... @@ -531,6 +531,7 @@ RTS_FUN_DECL(stg_raiseUnderflowzh);
    531 531
     RTS_FUN_DECL(stg_raiseOverflowzh);
    
    532 532
     RTS_FUN_DECL(stg_raiseIOzh);
    
    533 533
     RTS_FUN_DECL(stg_paniczh);
    
    534
    +RTS_FUN_DECL(stg_deepseqzh);
    
    534 535
     RTS_FUN_DECL(stg_keepAlivezh);
    
    535 536
     RTS_FUN_DECL(stg_absentErrorzh);
    
    536 537
     
    

  • rts/rts.cabal
    ... ... @@ -359,6 +359,7 @@ library
    359 359
           cmm-sources: Apply.cmm
    
    360 360
                        Compact.cmm
    
    361 361
                        ContinuationOps.cmm
    
    362
    +                   DeepSeq.cmm
    
    362 363
                        Exception.cmm
    
    363 364
                        HeapStackCheck.cmm
    
    364 365
                        Jumps_D.cmm
    

  • testsuite/tests/interface-stability/ghc-experimental-exports.stdout
    ... ... @@ -4454,6 +4454,11 @@ module Data.Tuple.Experimental where
    4454 4454
       data Unit# = ...
    
    4455 4455
       getSolo :: forall a. Solo a -> a
    
    4456 4456
     
    
    4457
    +module GHC.DeepSeq where
    
    4458
    +  -- Safety: None
    
    4459
    +  force :: forall a. a -> (GHC.Internal.Types.Bool, a)
    
    4460
    +  forceIO :: forall a. a -> GHC.Internal.Types.IO (GHC.Internal.Types.Bool, a)
    
    4461
    +
    
    4457 4462
     module GHC.PrimOps where
    
    4458 4463
       -- Safety: Unsafe
    
    4459 4464
       (*#) :: Int# -> Int# -> Int#
    
    ... ... @@ -4924,6 +4929,7 @@ module GHC.PrimOps where
    4924 4929
       decodeDouble_2Int# :: Double# -> (# Int#, Word#, Word#, Int# #)
    
    4925 4930
       decodeDouble_Int64# :: Double# -> (# Int64#, Int# #)
    
    4926 4931
       decodeFloat_Int# :: Float# -> (# Int#, Int# #)
    
    4932
    +  deepseq# :: forall a d. a -> State# d -> (# State# d, Int#, a #)
    
    4927 4933
       delay# :: forall d. Int# -> State# d -> State# d
    
    4928 4934
       divideDoubleX2# :: DoubleX2# -> DoubleX2# -> DoubleX2#
    
    4929 4935
       divideDoubleX4# :: DoubleX4# -> DoubleX4# -> DoubleX4#
    

  • testsuite/tests/interface-stability/ghc-prim-exports.stdout
    ... ... @@ -1592,6 +1592,7 @@ module GHC.Prim where
    1592 1592
       decodeDouble_2Int# :: Double# -> (# Int#, Word#, Word#, Int# #)
    
    1593 1593
       decodeDouble_Int64# :: Double# -> (# Int64#, Int# #)
    
    1594 1594
       decodeFloat_Int# :: Float# -> (# Int#, Int# #)
    
    1595
    +  deepseq# :: forall a d. a -> State# d -> (# State# d, Int#, a #)
    
    1595 1596
       delay# :: forall d. Int# -> State# d -> State# d
    
    1596 1597
       divideDoubleX2# :: DoubleX2# -> DoubleX2# -> DoubleX2#
    
    1597 1598
       divideDoubleX4# :: DoubleX4# -> DoubleX4# -> DoubleX4#
    
    ... ... @@ -3156,6 +3157,7 @@ module GHC.PrimopWrappers where
    3156 3157
       decodeDouble_2Int# :: GHC.Internal.Prim.Double# -> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Word#, GHC.Internal.Prim.Word#, GHC.Internal.Prim.Int# #)
    
    3157 3158
       decodeDouble_Int64# :: GHC.Internal.Prim.Double# -> (# GHC.Internal.Prim.Int64#, GHC.Internal.Prim.Int# #)
    
    3158 3159
       decodeFloat_Int# :: GHC.Internal.Prim.Float# -> (# GHC.Internal.Prim.Int#, GHC.Internal.Prim.Int# #)
    
    3160
    +  deepseq# :: forall a s. a -> GHC.Internal.Prim.State# s -> (# GHC.Internal.Prim.State# s, GHC.Internal.Prim.Int#, a #)
    
    3159 3161
       delay# :: forall s. GHC.Internal.Prim.Int# -> GHC.Internal.Prim.State# s -> GHC.Internal.Prim.State# s
    
    3160 3162
       divideFloat# :: GHC.Internal.Prim.Float# -> GHC.Internal.Prim.Float# -> GHC.Internal.Prim.Float#
    
    3161 3163
       double2Float# :: GHC.Internal.Prim.Double# -> GHC.Internal.Prim.Float#
    

  • testsuite/tests/primops/should_run/DeepSeqPrimOp.hs
    1
    +module Main (main) where
    
    2
    +
    
    3
    +import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar)
    
    4
    +import Control.Exception (SomeException, try)
    
    5
    +import Data.Array (Array, listArray)
    
    6
    +import Data.Array.IO (IOArray, newArray)
    
    7
    +import Data.IORef (IORef, newIORef)
    
    8
    +import GHC.DeepSeq (forceIO)
    
    9
    +import System.Mem.Weak (mkWeak)
    
    10
    +
    
    11
    +deepEvaluate :: a -> IO a
    
    12
    +deepEvaluate a = do
    
    13
    +  (_, a') <- forceIO a
    
    14
    +  pure a'
    
    15
    +
    
    16
    +deepEvaluateWithFlag :: a -> IO (Bool, a)
    
    17
    +deepEvaluateWithFlag = forceIO
    
    18
    +
    
    19
    +mkThunk :: Int -> Int
    
    20
    +mkThunk x = x + 1
    
    21
    +{-# NOINLINE mkThunk #-}
    
    22
    +
    
    23
    +boomVal :: Int
    
    24
    +boomVal = error "boom"
    
    25
    +{-# NOINLINE boomVal #-}
    
    26
    +
    
    27
    +funVal :: Int -> Int
    
    28
    +funVal _ = boomVal
    
    29
    +{-# NOINLINE funVal #-}
    
    30
    +
    
    31
    +main :: IO ()
    
    32
    +main = do
    
    33
    +  r1 <- try (deepEvaluate (1 :: Int, error "boom") >> pure ()) :: IO (Either SomeException ())
    
    34
    +  case r1 of
    
    35
    +    Left _  -> putStrLn "thunk-forced"
    
    36
    +    Right _ -> putStrLn "unexpected-no-exn"
    
    37
    +
    
    38
    +  r2 <- try (deepEvaluate funVal) :: IO (Either SomeException (Int -> Int))
    
    39
    +  case r2 of
    
    40
    +    Left _ -> putStrLn "unexpected-exn"
    
    41
    +    Right _ -> putStrLn "fun-ok"
    
    42
    +
    
    43
    +  (forced2, ()) <- deepEvaluateWithFlag ()
    
    44
    +  if not forced2
    
    45
    +    then putStrLn "noforce-ok"
    
    46
    +    else putStrLn "noforce-bad"
    
    47
    +
    
    48
    +  let v = (1 :: Int, mkThunk 2)
    
    49
    +  (forced3, v') <- deepEvaluateWithFlag v
    
    50
    +  if forced3 && snd v' == 3
    
    51
    +    then putStrLn "thunk-ok"
    
    52
    +    else putStrLn "unexpected"
    
    53
    +
    
    54
    +  let arr :: Array Int Int
    
    55
    +      arr = listArray (0, 0) [boomVal]
    
    56
    +  r3 <- try (deepEvaluate arr >> pure ()) :: IO (Either SomeException ())
    
    57
    +  case r3 of
    
    58
    +    Left _  -> putStrLn "array-thunk-forced"
    
    59
    +    Right _ -> putStrLn "array-unforced"
    
    60
    +
    
    61
    +  ioArr <- newArray (0, 0) boomVal :: IO (IOArray Int Int)
    
    62
    +  r4 <- try (deepEvaluate ioArr >> pure ()) :: IO (Either SomeException ())
    
    63
    +  case r4 of
    
    64
    +    Left _  -> putStrLn "ioarray-thunk-forced"
    
    65
    +    Right _ -> putStrLn "ioarray-unforced"
    
    66
    +
    
    67
    +  ref <- newIORef boomVal :: IO (IORef Int)
    
    68
    +  r5 <- try (deepEvaluate ref >> pure ()) :: IO (Either SomeException ())
    
    69
    +  case r5 of
    
    70
    +    Left _  -> putStrLn "ioref-thunk-forced"
    
    71
    +    Right _ -> putStrLn "ioref-unforced"
    
    72
    +
    
    73
    +  mvar <- newEmptyMVar :: IO (MVar Int)
    
    74
    +  putMVar mvar boomVal
    
    75
    +  r6 <- try (deepEvaluate mvar >> pure ()) :: IO (Either SomeException ())
    
    76
    +  case r6 of
    
    77
    +    Left _  -> putStrLn "mvar-thunk-forced"
    
    78
    +    Right _ -> putStrLn "mvar-unforced"
    
    79
    +
    
    80
    +  keyRef <- newIORef ()
    
    81
    +  weak <- mkWeak keyRef boomVal Nothing
    
    82
    +  r7 <- try (deepEvaluate weak >> pure ()) :: IO (Either SomeException ())
    
    83
    +  case r7 of
    
    84
    +    Left _  -> putStrLn "weak-thunk-forced"
    
    85
    +    Right _ -> putStrLn "weak-unforced"

  • testsuite/tests/primops/should_run/DeepSeqPrimOp.stdout
    1
    +thunk-forced
    
    2
    +fun-ok
    
    3
    +noforce-ok
    
    4
    +thunk-ok
    
    5
    +array-thunk-forced
    
    6
    +ioarray-thunk-forced
    
    7
    +ioref-thunk-forced
    
    8
    +mvar-thunk-forced
    
    9
    +weak-thunk-forced

  • testsuite/tests/primops/should_run/all.T
    ... ... @@ -17,6 +17,7 @@ test('T13825-compile', normal, compile_and_run, [''])
    17 17
     test('T16164', normal, compile_and_run, [''])
    
    18 18
     test('ShowPrim', normal, compile_and_run, [''])
    
    19 19
     test('T12492', normal, compile_and_run, [''])
    
    20
    +test('DeepSeqPrimOp', [js_skip, extra_ways(['ghci','ghci-opt']), extra_hc_opts('-package ghc-experimental')], compile_and_run, [''])
    
    20 21
     
    
    21 22
     test('ArithInt8', normal, compile_and_run, [''])
    
    22 23
     test('ArithWord8', normal, compile_and_run, [''])