Looking at how go compiles/optimizes a couple of common constructs, which I boiled down to a simple '\t' replacement loop.
```
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/jvhj5aMy 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?