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.

Reply via email to