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/a95283eb-cef8-448f-a53c-f6162150d243n%40googlegroups.com.