> My questions: Is any form of loop/invariant-hoisting performed?

Yes, but not much. We hoist simple things like constants and spill 
restores. The big kahuna, loads, are typically not lifted because they 
depend on memory and we don't have the alias analysis to prove that that is 
safe.

We definitely don't do any loop duplication + specialization.

 > Does the compiler ever produce conditional moves? Where would I even 
look in the (go src) code?

Yes. Look in cmd/compile/internal/ssa/branchelim.go. We generate generic 
CondSelect operations there, then lowering passes convert to 
machine-specific conditional moves.
It looks like in your case we don't generate conditional moves because 
amd64 doesn't have byte-sized conditional moves (see canCondSelect in the 
mentioned file). If you make c an int instead of a byte, with the 
appropriate casts, then it works.
I think that no-byte-cmovs restriction can be lifted. It might cause a 
partial register stall, but maybe that's ok.


On Tuesday, November 10, 2020 at 4:05:17 PM UTC-8 
oliver...@superevilmegacorp.com wrote:

> Looking at how go compiles/optimizes a couple of common constructs, which 
> I boiled down to a simple '\t' replacement loop.
>
> godbolt of sources: https://go.godbolt.org/z/Pnf3vh
>
> ```
> func v1(s []byte, detab bool) (d []byte) {
>   d = make([]byte, len(s))
>   for i := 0; i < len(s); i++ {
>     char := s[i]
>     if detab && char == '\t' {
>       char = ' '
>     }
>     d[i] = char
>   }
>   return d
> }
> ```
>
> which is branch happy:
>
> ```
> v1_pc93:
>         movb    DIB, (AX)(SI*1)
>         incq    SI
> v1_pc100:
>         cmpq    SI, CX
>         jge     v1_pc126
>         movblzx (BX)(SI*1), DI
>         testb   DL, DL
>         jeq     v1_pc93
>         cmpb    DIB, $9
>         jne     v1_pc93
>         movl    $32, DI
>         jmp     v1_pc93
> ```
>
> Coming from a C background, my first surprise was that the loop invariant 
> wasn't hoisted, it didn't generate two versions of the loop body predicated 
> on 'detab', but my second surprise was that it generates branch operations 
> rather than a simple conditional move.
>
> I do sort of love that go rewards you a little for letting it do a bit 
> more of the lifting:
>
> ```
> func v2(s []byte, detab bool) (d []byte) {
>   d = make([]byte, len(s))
>   for i := 0; i < len(s); i++ {
>     if detab && s[i] == '\t' {
>       d[i] = ' '
>     } else {
>       d[i] = s[i]
>     }
>   }
>   return d
> }
> ```
>
> but that's countered by the failure to eliminate the invariant, which I 
> can do manually thus:
>
> ```
> func v3(s []byte, detab bool) (d []byte) {
>   d = make([]byte, len(s))
>   tabReplacement := byte('\t')
>   if detab {
>       tabReplacement = byte(' ')
>   }
>   for i := 0; i < len(s); i++ {
>     if s[i] == '\t' {
>       d[i] = tabReplacement
>     } else {
>       d[i] = s[i]
>     }
>   }
>   return d
> }
> ```
>
> With manual unrolling to reduce the conditions, I still don't see the 
> hoped-for cmov:
>
> ```
> func v4(s []byte, detab bool) (d []byte) {
>     d = make([]byte, len(s))
>     if detab {
>         for i := 0; i < len(s); i++ {
>             c := s[i]
>             if c == '\t' {
>                 c = ' '
>             }
>             d[i] = c
>         }
>     } else {
>         for i := 0; i < len(s); i++ {
>             d[i] = s[i]
>         }
>     }
>     return d
> }
> ```
>
> produces
>
> ```
>         jmp     v4_pc105
> v4_pc98:
>         movb    SIB, (AX)(BX*1)
>         incq    BX
> v4_pc105:
>         cmpq    BX, CX
>         jge     v4_pc127
>         movblzx (DX)(BX*1), SI
>         cmpb    SIB, $9
>         jne     v4_pc98
>         movl    $32, SI
>         jmp     v4_pc98
> ```
>
> what I was hoping to produce was:
>
> ```
> v4_pc98:
>         cmpq    BX, CX
>         jge         v4_pc127
>         movblzx (DX)(BX*1), SI
>         cmpb    SIB, $9
>         cmovl   $32, SI
>         movb    SIB, (AX)(BX*1)
>         incq    BX
>         jmp     v4_pc98
> ```
>
> anything along the lines of: https://gcc.godbolt.org/z/jvhj5a
>
> My questions: Is any form of loop/invariant-hoisting performed? Does the 
> compiler ever produce conditional moves? Where would I even look in the (go 
> src) code?
>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/e82d04e2-4ecf-4a9e-809e-12ffaf329f98n%40googlegroups.com.

Reply via email to