Repository: go
Issue: cmd/compile: optimize f32Slice[i] += 1 across math.Float32bits, Float32frombits
Labels: NeedsFix,compiler/runtime

I'm not sure how often this arises in practice, but it did come up in https://go-review.googlesource.com/#/c/29691/ in a vector rasterizer

Roughly speaking, in func floatingAccumulateMask, I'm accumulating the elements of a []float32 and storing the (scaled) cumulative sums in an []uint32.

I don't actually need both of the individual and cumulative values at the same time. If the two slices were both []float32 or both []uint32, then I could halve the amount of memory that I need (and possibly have better cache access patterns) by writing the output elements in-place over the input elements.

I can actually still do this, even without using unsafe, with just one slice (of type []uint32), and sprinking some math.Float32bits and math.Float32frombits throughout my float32 code.

This works, in that it produces the correct output, but there is a performance penalty. In a simpler repro case, suppose that I had these global variables:

```
f32Slice []float32
u32Slice []uint32
```

and these three lines of code:

```
f32Slice[0] += 1
u32Slice[0] = uint32(1 + int32(u32Slice[0]))
u32Slice[0] = math.Float32bits(1 + math.Float32frombits(u32Slice[0]))
```

The GOARCH=amd64 codegen for each of the three lines are:

```
48 8b 05 b1 71 09 00    mov    0x971b1(%rip),%rax        # 498298 <main.f32Slice+0x8>
48 8b 0d a2 71 09 00    mov    0x971a2(%rip),%rcx        # 498290 <main.f32Slice>
48 85 c0                test   %rax,%rax
76 3a                   jbe    40112d <main.main+0x12d>
f3 0f 10 01             movss  (%rcx),%xmm0
f3 0f 10 0d 15 cc 06    movss  0x6cc15(%rip),%xmm1        # 46dd14 <$f32.3f800000>
00 
f3 0f 58 c8             addss  %xmm0,%xmm1
f3 0f 11 09             movss  %xmm1,(%rcx)
```

```
48 8b 05 fe 71 09 00    mov    0x971fe(%rip),%rax        # 4982b0 <main.u32Slice>
48 8b 0d ff 71 09 00    mov    0x971ff(%rip),%rcx        # 4982b8 <main.u32Slice+0x8>
48 85 c9                test   %rcx,%rcx
76 76                   jbe    401134 <main.main+0x134>
8b 08                   mov    (%rax),%ecx
ff c1                   inc    %ecx
89 08                   mov    %ecx,(%rax)
```

```
48 8b 05 6c 72 09 00    mov    0x9726c(%rip),%rax        # 4982b0 <main.u32Slice>
48 8b 0d 6d 72 09 00    mov    0x9726d(%rip),%rcx        # 4982b8 <main.u32Slice+0x8>
48 85 c9                test   %rcx,%rcx
0f 86 e7 00 00 00       jbe    40113b <main.main+0x13b>
8b 00                   mov    (%rax),%eax
89 44 24 0c             mov    %eax,0xc(%rsp)
f3 0f 10 44 24 0c       movss  0xc(%rsp),%xmm0
f3 0f 10 0d ac cc 06    movss  0x6ccac(%rip),%xmm1        # 46dd14 <$f32.3f800000>
00 
f3 0f 58 c1             addss  %xmm1,%xmm0
f3 0f 11 44 24 08       movss  %xmm0,0x8(%rsp)
8b 44 24 08             mov    0x8(%rsp),%eax
48 8b 0d 33 72 09 00    mov    0x97233(%rip),%rcx        # 4982b0 <main.u32Slice>
48 8b 15 34 72 09 00    mov    0x97234(%rip),%rdx        # 4982b8 <main.u32Slice+0x8>
48 85 d2                test   %rdx,%rdx
0f 86 ae 00 00 00       jbe    40113b <main.main+0x13b>
89 01                   mov    %eax,(%rcx)
```

The codegen for the first two lines are efficient. The codegen for the third line could be better in two ways. First, there's an unnecessary bounce via the stack from memory to XMM0:

```
8b 00                   mov    (%rax),%eax
89 44 24 0c             mov    %eax,0xc(%rsp)
f3 0f 10 44 24 0c       movss  0xc(%rsp),%xmm0
```

and likewise on the way back. Second, there are two bounds checks instead of one, but the second is redundant.

I am not a compiler person, but it looks to me like the Float32{,from}bits calls are treated as black box functions and not like a (no-op) uint32 to int32 conversion until too late in the codegen for e.g. the relevant bounds check elimination.

I'll let y'all decide if this is a dupe of issue #17069.