<div dir="auto">In this example: why would it stop being a join point ? </div><div dir="auto"><br></div><div dir="auto">Admittedly, my intuition might be skewed by my own ideas about how join points are sortah a semantic special case of other constructs. </div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Nov 20, 2021 at 4:17 PM Simon Peyton Jones via ghc-devs <<a href="mailto:ghc-devs@haskell.org">ghc-devs@haskell.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)">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<br>
<br>
join j1 x = x+1<br>
in case v of<br>
A -> f (join j2 x = x+1 in ...j2...)<br>
B -> ....j1...<br>
C -> ....j1...<br>
<br>
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.<br>
<br>
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.<br>
<br>
Do open a ticket!<br>
<br>
Simon<br>
<br>
PS: I am leaving Microsoft at the end of November 2021, at which point <a href="mailto:simonpj@microsoft.com" target="_blank">simonpj@microsoft.com</a> will cease to work. Use <a href="mailto:simon.peytonjones@gmail.com" target="_blank">simon.peytonjones@gmail.com</a> instead. (For now, it just forwards to <a href="mailto:simonpj@microsoft.com" target="_blank">simonpj@microsoft.com</a>.)<br>
<br>
| -----Original Message-----<br>
| From: ghc-devs <<a href="mailto:ghc-devs-bounces@haskell.org" target="_blank">ghc-devs-bounces@haskell.org</a>> On Behalf Of Viktor<br>
| Dukhovni<br>
| Sent: 20 November 2021 00:57<br>
| To: <a href="mailto:ghc-devs@haskell.org" target="_blank">ghc-devs@haskell.org</a><br>
| Subject: [EXTERNAL] Unexpected duplicate join points in "Core" output?<br>
| <br>
| The below "Core" output from "ghc -O2" (9.2/8.10) for the attached<br>
| program shows seemingly rendundant join points:<br>
| <br>
| join {<br>
| exit :: State# RealWorld -> (# State# RealWorld, () #)<br>
| exit (ipv :: State# RealWorld) = jump $s$j ipv } in<br>
| <br>
| join {<br>
| exit1 :: State# RealWorld -> (# State# RealWorld, () #)<br>
| exit1 (ipv :: State# RealWorld) = jump $s$j ipv } in<br>
| <br>
| that are identical in all but name. These correspond to fallthrough to<br>
| the "otherwise" case in:<br>
| <br>
| ...<br>
| | acc < q || (acc == q && d <= 5)<br>
| -> loop (ptr `plusPtr` 1) (acc * 10 + d)<br>
| | otherwise -> return Nothing<br>
| <br>
| but it seems that the generated X86_64 code (also below) ultimately<br>
| consolidates these into a single target... Is that why it is harmless<br>
| to leave these duplicated in the generated "Core"?<br>
| <br>
| [ Separately, in the generated machine code, it'd also be nice to avoid<br>
| comparing the same "q" with the accumulator twice. A single load and<br>
| compare should I think be enough, as I'd expect the status flags to<br>
| persist across the jump the second test.<br>
| <br>
| This happens to not be performance critical in my case, because most<br>
| calls should satisfy the first test, but generally I think that 3-way<br>
| "a < b", "a == b", "a > b" branches ideally avoid comparing twice...<br>
| ]<br>
| <br>
| ======== Associated Core output<br>
| <br>
| -- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}<br>
| main2 :: Addr#<br>
| main2 = "12345678901234567890 junk"#<br>
| <br>
| -- RHS size: {terms: 129, types: 114, coercions: 0, joins: 6/8}<br>
| main1 :: State# RealWorld -> (# State# RealWorld, () #)<br>
| main1<br>
| = \ (eta :: State# RealWorld) -><br>
| let {<br>
| end :: Addr#<br>
| end = plusAddr# main2 25# } in<br>
| join {<br>
| $s$j :: State# RealWorld -> (# State# RealWorld, () #)<br>
| $s$j _ = hPutStr2 stdout $fShowMaybe4 True eta } in<br>
| join {<br>
| exit :: State# RealWorld -> (# State# RealWorld, () #)<br>
| exit (ipv :: State# RealWorld) = jump $s$j ipv } in<br>
| join {<br>
| exit1 :: State# RealWorld -> (# State# RealWorld, () #)<br>
| exit1 (ipv :: State# RealWorld) = jump $s$j ipv } in<br>
| join {<br>
| exit2<br>
| :: Addr# -> Word# -> State# RealWorld -> (# State#<br>
| RealWorld, () #)<br>
| exit2 (ww :: Addr#) (ww1 :: Word#) (ipv :: State#<br>
| RealWorld)<br>
| = case eqAddr# ww main2 of {<br>
| __DEFAULT -><br>
| hPutStr2<br>
| stdout<br>
| (++<br>
| $fShowMaybe1<br>
| (case $w$cshowsPrec3 11# (integerFromWord#<br>
| ww1) [] of<br>
| { (# ww3, ww4 #) -><br>
| : ww3 ww4<br>
| }))<br>
| True<br>
| eta;<br>
| 1# -> jump $s$j ipv<br>
| } } in<br>
| joinrec {<br>
| $wloop<br>
| :: Addr# -> Word# -> State# RealWorld -> (# State#<br>
| RealWorld, () #)<br>
| $wloop (ww :: Addr#) (ww1 :: Word#) (w :: State# RealWorld)<br>
| = join {<br>
| getDigit :: State# RealWorld -> (# State# RealWorld,<br>
| () #)<br>
| getDigit (eta1 :: State# RealWorld)<br>
| = case eqAddr# ww end of {<br>
| __DEFAULT -><br>
| case readWord8OffAddr# ww 0# eta1 of { (#<br>
| ipv, ipv1 #) -><br>
| let {<br>
| ipv2 :: Word#<br>
| ipv2 = minusWord# (word8ToWord# ipv1) 48##<br>
| } in<br>
| case gtWord# ipv2 9## of {<br>
| __DEFAULT -><br>
| case ltWord# ww1 1844674407370955161## of<br>
| {<br>
| __DEFAULT -><br>
| case ww1 of {<br>
| __DEFAULT -> jump exit ipv;<br>
| 1844674407370955161## -><br>
| case leWord# ipv2 5## of {<br>
| __DEFAULT -> jump exit1 ipv;<br>
| 1# -><br>
| jump $wloop<br>
| (plusAddr# ww 1#)<br>
| (plusWord#<br>
| 18446744073709551610## ipv2)<br>
| ipv<br>
| }<br>
| };<br>
| 1# -><br>
| jump $wloop<br>
| (plusAddr# ww 1#) (plusWord#<br>
| (timesWord# ww1 10##) ipv2) ipv<br>
| };<br>
| 1# -> jump exit2 ww ww1 ipv<br>
| }<br>
| };<br>
| 1# -> jump exit2 ww ww1 eta1<br>
| } } in<br>
| jump getDigit w; } in<br>
| jump $wloop main2 0## realWorld#<br>
| <br>
| ======== Executable disassembly<br>
| <br>
| The jumps at "-1->" and "-2->" that correspond that "otherwise" have<br>
| the same target. The duplicate "load+cmp" with "q" is at "-3->" and "-<br>
| 4->":<br>
| <br>
| 0000000000408de8 <Main_main1_info>:<br>
| 408de8: 48 8d 45 e8 lea -0x18(%rbp),%rax<br>
| 408dec: 4c 39 f8 cmp %r15,%rax<br>
| 408def: 0f 82 c8 00 00 00 jb 408ebd<br>
| <Main_main1_info+0xd5><br>
| 408df5: b8 79 dd 77 00 mov $0x77dd79,%eax<br>
| 408dfa: 31 db xor %ebx,%ebx<br>
| 408dfc: b9 60 dd 77 00 mov $0x77dd60,%ecx<br>
| 408e01: 48 39 c1 cmp %rax,%rcx<br>
| 408e04: 74 66 je 408e6c<br>
| <Main_main1_info+0x84><br>
| 408e06: 0f b6 11 movzbl (%rcx),%edx<br>
| 408e09: 48 83 c2 d0 add<br>
| $0xffffffffffffffd0,%rdx<br>
| 408e0d: 48 83 fa 09 cmp $0x9,%rdx<br>
| 408e11: 77 59 ja 408e6c<br>
| <Main_main1_info+0x84><br>
| -3-> 408e13: 48 be 99 99 99 99 99 mov<br>
| $0x1999999999999999,%rsi<br>
| 408e1a: 99 99 19<br>
| 408e1d: 48 39 f3 cmp %rsi,%rbx<br>
| 408e20: 73 0c jae 408e2e<br>
| <Main_main1_info+0x46><br>
| 408e22: 48 6b db 0a imul $0xa,%rbx,%rbx<br>
| 408e26: 48 01 d3 add %rdx,%rbx<br>
| 408e29: 48 ff c1 inc %rcx<br>
| 408e2c: eb d3 jmp 408e01<br>
| <Main_main1_info+0x19><br>
| -4-> 408e2e: 48 be 99 99 99 99 99 mov<br>
| $0x1999999999999999,%rsi<br>
| 408e35: 99 99 19<br>
| 408e38: 48 39 f3 cmp %rsi,%rbx<br>
| -1-> 408e3b: 75 49 jne 408e86<br>
| <Main_main1_info+0x9e><br>
| 408e3d: 48 83 fa 05 cmp $0x5,%rdx<br>
| -2-> 408e41: 77 43 ja 408e86<br>
| <Main_main1_info+0x9e><br>
| 408e43: 48 8d 5a fa lea -0x6(%rdx),%rbx<br>
| 408e47: 48 ff c1 inc %rcx<br>
| 408e4a: eb b5 jmp 408e01<br>
| <Main_main1_info+0x19><br>
| 408e4c: 0f 1f 40 00 nopl 0x0(%rax)<br>
| 408e50: c2 00 00 retq $0x0<br>
| <br>
| --<br>
| Viktor.<br>
_______________________________________________<br>
ghc-devs mailing list<br>
<a href="mailto:ghc-devs@haskell.org" target="_blank">ghc-devs@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs</a><br>
</blockquote></div></div>