[EXTERNAL] Unexpected duplicate join points in "Core" output?

Carter Schonwald carter.schonwald at gmail.com
Sun Nov 21 11:53:53 UTC 2021


In this example: why would it stop being a join point ?

Admittedly, my intuition might be skewed by my own ideas about how join
points are sortah a semantic special case of other constructs.

On Sat, Nov 20, 2021 at 4:17 PM Simon Peyton Jones via ghc-devs <
ghc-devs at haskell.org> wrote:

> There is absolutely no reason not to common-up those to join points.  But
> we can't common up some join points when we could if they were let's.
> Consider
>
> join j1 x = x+1
> in case v of
>       A -> f (join j2 x = x+1 in ...j2...)
>       B -> ....j1...
>       C -> ....j1...
>
> Even though j2 is identical to j1's, we can't eliminate j2 in favour of j1
> because then j1 wouldn't be a join point any more.
>
> GHC.Core.Opt.CSE is conservative at the moment, and never CSE's *any* join
> point.  It would not be hard to make it clever enough to CSE join points,
> but no one has yet done it.
>
> Do open a ticket!
>
> Simon
>
> PS: I am leaving Microsoft at the end of November 2021, at which point
> simonpj at microsoft.com will cease to work.  Use simon.peytonjones at gmail.com
> instead.  (For now, it just forwards to simonpj at microsoft.com.)
>
> | -----Original Message-----
> | From: ghc-devs <ghc-devs-bounces at haskell.org> On Behalf Of Viktor
> | Dukhovni
> | Sent: 20 November 2021 00:57
> | To: ghc-devs at haskell.org
> | Subject: [EXTERNAL] Unexpected duplicate join points in "Core" output?
> |
> | The below "Core" output from "ghc -O2" (9.2/8.10) for the attached
> | program shows seemingly rendundant join points:
> |
> |       join {
> |         exit :: State# RealWorld -> (# State# RealWorld, () #)
> |         exit (ipv :: State# RealWorld) = jump $s$j ipv } in
> |
> |       join {
> |         exit1 :: State# RealWorld -> (# State# RealWorld, () #)
> |         exit1 (ipv :: State# RealWorld) = jump $s$j ipv } in
> |
> | that are identical in all but name.  These correspond to fallthrough to
> | the "otherwise" case in:
> |
> |            ...
> |            | acc < q || (acc == q && d <= 5)
> |              -> loop (ptr `plusPtr` 1) (acc * 10 + d)
> |            | otherwise -> return Nothing
> |
> | but it seems that the generated X86_64 code (also below) ultimately
> | consolidates these into a single target... Is that why it is harmless
> | to leave these duplicated in the generated "Core"?
> |
> | [ Separately, in the generated machine code, it'd also be nice to avoid
> |   comparing the same "q" with the accumulator twice.  A single load and
> |   compare should I think be enough, as I'd expect the status flags to
> |   persist across the jump the second test.
> |
> |   This happens to not be performance critical in my case, because most
> |   calls should satisfy the first test, but generally I think that 3-way
> |   "a < b", "a == b", "a > b" branches ideally avoid comparing twice...
> | ]
> |
> | ======== Associated Core output
> |
> |     -- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
> |     main2 :: Addr#
> |     main2 = "12345678901234567890 junk"#
> |
> |     -- RHS size: {terms: 129, types: 114, coercions: 0, joins: 6/8}
> |     main1 :: State# RealWorld -> (# State# RealWorld, () #)
> |     main1
> |       = \ (eta :: State# RealWorld) ->
> |           let {
> |             end :: Addr#
> |             end = plusAddr# main2 25# } in
> |           join {
> |             $s$j :: State# RealWorld -> (# State# RealWorld, () #)
> |             $s$j _ = hPutStr2 stdout $fShowMaybe4 True eta } in
> |           join {
> |             exit :: State# RealWorld -> (# State# RealWorld, () #)
> |             exit (ipv :: State# RealWorld) = jump $s$j ipv } in
> |           join {
> |             exit1 :: State# RealWorld -> (# State# RealWorld, () #)
> |             exit1 (ipv :: State# RealWorld) = jump $s$j ipv } in
> |           join {
> |             exit2
> |               :: Addr# -> Word# -> State# RealWorld -> (# State#
> | RealWorld, () #)
> |             exit2 (ww :: Addr#) (ww1 :: Word#) (ipv :: State#
> | RealWorld)
> |               = case eqAddr# ww main2 of {
> |                   __DEFAULT ->
> |                     hPutStr2
> |                       stdout
> |                       (++
> |                          $fShowMaybe1
> |                          (case $w$cshowsPrec3 11# (integerFromWord#
> | ww1) [] of
> |                           { (# ww3, ww4 #) ->
> |                           : ww3 ww4
> |                           }))
> |                       True
> |                       eta;
> |                   1# -> jump $s$j ipv
> |                 } } in
> |           joinrec {
> |             $wloop
> |               :: Addr# -> Word# -> State# RealWorld -> (# State#
> | RealWorld, () #)
> |             $wloop (ww :: Addr#) (ww1 :: Word#) (w :: State# RealWorld)
> |               = join {
> |                   getDigit :: State# RealWorld -> (# State# RealWorld,
> | () #)
> |                   getDigit (eta1 :: State# RealWorld)
> |                     = case eqAddr# ww end of {
> |                         __DEFAULT ->
> |                           case readWord8OffAddr# ww 0# eta1 of { (#
> | ipv, ipv1 #) ->
> |                           let {
> |                             ipv2 :: Word#
> |                             ipv2 = minusWord# (word8ToWord# ipv1) 48##
> | } in
> |                           case gtWord# ipv2 9## of {
> |                             __DEFAULT ->
> |                               case ltWord# ww1 1844674407370955161## of
> | {
> |                                 __DEFAULT ->
> |                                   case ww1 of {
> |                                     __DEFAULT -> jump exit ipv;
> |                                     1844674407370955161## ->
> |                                       case leWord# ipv2 5## of {
> |                                         __DEFAULT -> jump exit1 ipv;
> |                                         1# ->
> |                                           jump $wloop
> |                                             (plusAddr# ww 1#)
> |                                             (plusWord#
> | 18446744073709551610## ipv2)
> |                                             ipv
> |                                       }
> |                                   };
> |                                 1# ->
> |                                   jump $wloop
> |                                     (plusAddr# ww 1#) (plusWord#
> | (timesWord# ww1 10##) ipv2) ipv
> |                               };
> |                             1# -> jump exit2 ww ww1 ipv
> |                           }
> |                           };
> |                         1# -> jump exit2 ww ww1 eta1
> |                       } } in
> |                 jump getDigit w; } in
> |           jump $wloop main2 0## realWorld#
> |
> | ======== Executable disassembly
> |
> | The jumps at "-1->" and "-2->" that correspond that "otherwise" have
> | the same target.  The duplicate "load+cmp" with "q" is at "-3->" and "-
> | 4->":
> |
> |     0000000000408de8 <Main_main1_info>:
> |       408de8:       48 8d 45 e8             lea    -0x18(%rbp),%rax
> |       408dec:       4c 39 f8                cmp    %r15,%rax
> |       408def:       0f 82 c8 00 00 00       jb     408ebd
> | <Main_main1_info+0xd5>
> |       408df5:       b8 79 dd 77 00          mov    $0x77dd79,%eax
> |       408dfa:       31 db                   xor    %ebx,%ebx
> |       408dfc:       b9 60 dd 77 00          mov    $0x77dd60,%ecx
> |       408e01:       48 39 c1                cmp    %rax,%rcx
> |       408e04:       74 66                   je     408e6c
> | <Main_main1_info+0x84>
> |       408e06:       0f b6 11                movzbl (%rcx),%edx
> |       408e09:       48 83 c2 d0             add
> | $0xffffffffffffffd0,%rdx
> |       408e0d:       48 83 fa 09             cmp    $0x9,%rdx
> |       408e11:       77 59                   ja     408e6c
> | <Main_main1_info+0x84>
> | -3->  408e13:       48 be 99 99 99 99 99    mov
> | $0x1999999999999999,%rsi
> |       408e1a:       99 99 19
> |       408e1d:       48 39 f3                cmp    %rsi,%rbx
> |       408e20:       73 0c                   jae    408e2e
> | <Main_main1_info+0x46>
> |       408e22:       48 6b db 0a             imul   $0xa,%rbx,%rbx
> |       408e26:       48 01 d3                add    %rdx,%rbx
> |       408e29:       48 ff c1                inc    %rcx
> |       408e2c:       eb d3                   jmp    408e01
> | <Main_main1_info+0x19>
> | -4->  408e2e:       48 be 99 99 99 99 99    mov
> | $0x1999999999999999,%rsi
> |       408e35:       99 99 19
> |       408e38:       48 39 f3                cmp    %rsi,%rbx
> | -1->  408e3b:       75 49                   jne    408e86
> | <Main_main1_info+0x9e>
> |       408e3d:       48 83 fa 05             cmp    $0x5,%rdx
> | -2->  408e41:       77 43                   ja     408e86
> | <Main_main1_info+0x9e>
> |       408e43:       48 8d 5a fa             lea    -0x6(%rdx),%rbx
> |       408e47:       48 ff c1                inc    %rcx
> |       408e4a:       eb b5                   jmp    408e01
> | <Main_main1_info+0x19>
> |       408e4c:       0f 1f 40 00             nopl   0x0(%rax)
> |       408e50:       c2 00 00                retq   $0x0
> |
> | --
> |     Viktor.
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20211121/a6d4e95e/attachment.html>


More information about the ghc-devs mailing list