Sebastian Graf pushed to branch wip/sg/enter-data at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • compiler/GHC/StgToCmm/Expr.hs
    ... ... @@ -36,6 +36,8 @@ import GHC.Cmm.BlockId
    36 36
     import GHC.Cmm hiding ( succ )
    
    37 37
     import GHC.Cmm.Info
    
    38 38
     import GHC.Cmm.Utils ( cmmTagMask, mkWordCLit )
    
    39
    +import GHC.Cmm.CLabel ( mkCmmCodeLabel )
    
    40
    +import GHC.Unit ( rtsUnitId )
    
    39 41
     import GHC.Platform.Tag ( mAX_PTR_TAG )
    
    40 42
     import GHC.Core
    
    41 43
     import GHC.Core.DataCon
    
    ... ... @@ -1190,22 +1192,16 @@ emitEnter fun = do
    1190 1192
       ; adjustHpBackwards
    
    1191 1193
       ; sequel      <- getSequel
    
    1192 1194
       ; updfr_off   <- getUpdFrameOff
    
    1193
    -  ; align_check <- stgToCmmAlignCheck <$> getStgToCmmConfig
    
    1194 1195
       ; case sequel of
    
    1195
    -      -- For a return, we have the option of generating a tag-test or
    
    1196
    -      -- not.  If the value is tagged, we can return directly, which
    
    1197
    -      -- is quicker than entering the value.  This is a code
    
    1198
    -      -- size/speed trade-off: when optimising for speed rather than
    
    1199
    -      -- size we could generate the tag test.
    
    1200
    -      --
    
    1201
    -      -- Right now, we do what the old codegen did, and omit the tag
    
    1202
    -      -- test, just generating an enter.
    
    1196
    +      -- For a return we jump to stg_enter_data, which returns a tagged
    
    1197
    +      -- (hence evaluated) value to our caller and enters an untagged
    
    1198
    +      -- closure. getCallMethod picks EnterIt only for values whose type
    
    1199
    +      -- rules out functions, which is what that stub is specialised to.
    
    1200
    +      -- See Note [Evaluating a value in tail position] in rts/Apply.cmm.
    
    1203 1201
           Return -> do
    
    1204
    -        { let entry = entryCode platform
    
    1205
    -                $ closureInfoPtr platform align_check
    
    1206
    -                $ CmmReg (nodeReg platform)
    
    1207
    -        ; emit $ mkJump profile NativeNodeCall entry
    
    1208
    -                        [cmmUntag platform fun] updfr_off
    
    1202
    +        { let enter_data = CmmLit (CmmLabel
    
    1203
    +                             (mkCmmCodeLabel rtsUnitId (fsLit "stg_enter_data")))
    
    1204
    +        ; emit $ mkJump profile NativeNodeCall enter_data [fun] updfr_off
    
    1209 1205
             ; return AssignedDirectly
    
    1210 1206
             }
    
    1211 1207
     
    

  • rts/Apply.cmm
    ... ... @@ -47,6 +47,55 @@ import CLOSURE stg_apply_interp_info;
    47 47
     import CLOSURE stg_restore_cccs_eval_info;
    
    48 48
     #endif
    
    49 49
     
    
    50
    +/* Note [Evaluating a value in tail position]
    
    51
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    52
    + * stg_enter_data evaluates a closure that is a thunk, a constructor, or an
    
    53
    + * indirection to one, and returns it to the caller's continuation. This is
    
    54
    + * stg_ap_0_fast (below) specialised to that domain: the FUN/PAP/BCO arms of
    
    55
    + * the generic evaluation are unreachable there, and an indirection's own
    
    56
    + * entry code follows the indirection, so what remains is the tag test and an
    
    57
    + * entry jump. emitEnter (GHC.StgToCmm.Expr) jumps here for a tail evaluation,
    
    58
    + * which getCallMethod reaches only for values whose type rules out functions.
    
    59
    + *
    
    60
    + * Three shapes evaluate such a value. Measured on nofib imaginary+spectral
    
    61
    + * (74 benchmarks, cachegrind instruction counts, both sides built with
    
    62
    + * -fproc-alignment=64 so that code placement is held fixed):
    
    63
    + *
    
    64
    + *   - Enter unconditionally, reading the entry code out of the closure. This
    
    65
    + *     reaches the entry code of an evaluated value, which the enter-taggable
    
    66
    + *     invariant rules out. It also costs a load of the info pointer feeding
    
    67
    + *     an indirect jump, plus the entry code's own indirect jump to the
    
    68
    + *     continuation, whenever the value is already evaluated.
    
    69
    + *   - Jump to stg_ap_0_fast. Adds a load of the closure type and a
    
    70
    + *     bounds-checked switch on it, both dead on this domain: +0.19%
    
    71
    + *     instructions over this stub.
    
    72
    + *   - Jump to this stub. +0.07% instructions over entering unconditionally,
    
    73
    + *     allocations unchanged. The tag test mostly fails, because values whose
    
    74
    + *     tag is statically known are routed to InferedReturnIt by tag inference
    
    75
    + *     before they reach here, leaving mostly genuine thunks: fft2 +0.36%,
    
    76
    + *     fft +0.24%.
    
    77
    + *
    
    78
    + * Cycles move in both directions and by more than the instruction counts,
    
    79
    + * because what the test replaces is a load feeding an indirect jump rather
    
    80
    + * than straight-line work. Where the value is usually evaluated, primetest
    
    81
    + * runs -1.5% and exact-reals -1.0%; where it is usually a thunk, fft2 runs
    
    82
    + * +0.9% and fft +0.4%.
    
    83
    + *
    
    84
    + * The stub keeps the test out of the code generator's per-site output. Open
    
    85
    + * coding it at each call site costs a temporary, two basic blocks and an
    
    86
    + * explicit return there, which compiling T13960 pays for at around +3%
    
    87
    + * allocations; through the stub the compiler emits one jump, as it did when
    
    88
    + * it entered unconditionally.
    
    89
    + */
    
    90
    +
    
    91
    +stg_enter_data ( P_ x )
    
    92
    +{
    
    93
    +    if (GETTAG(x) != 0) {
    
    94
    +        return (x);
    
    95
    +    }
    
    96
    +    jump %GET_ENTRY(x) (x);
    
    97
    +}
    
    98
    +
    
    50 99
     /* ----------------------------------------------------------------------------
    
    51 100
      * Evaluate a closure and return it.
    
    52 101
      *
    

  • rts/RtsSymbols.c
    ... ... @@ -842,6 +842,7 @@ extern char **environ;
    842 842
           SymI_HasDataProto(stg_ap_ppppp_info)                                  \
    
    843 843
           SymI_HasDataProto(stg_ap_pppppp_info)                                 \
    
    844 844
           SymI_HasDataProto(stg_ap_0_fast)                                      \
    
    845
    +      SymI_HasDataProto(stg_enter_data)                                     \
    
    845 846
           SymI_HasDataProto(stg_ap_v_fast)                                      \
    
    846 847
           SymI_HasDataProto(stg_ap_f_fast)                                      \
    
    847 848
           SymI_HasDataProto(stg_ap_d_fast)                                      \
    

  • rts/include/stg/MiscClosures.h
    ... ... @@ -300,6 +300,7 @@ RTS_RET(stg_ap_ppppp);
    300 300
     RTS_RET(stg_ap_pppppp);
    
    301 301
     
    
    302 302
     RTS_FUN_DECL(stg_ap_0_fast);
    
    303
    +RTS_FUN_DECL(stg_enter_data);
    
    303 304
     RTS_FUN_DECL(stg_ap_v_fast);
    
    304 305
     RTS_FUN_DECL(stg_ap_f_fast);
    
    305 306
     RTS_FUN_DECL(stg_ap_d_fast);