Repository: go
Issue: cmd/compile: simplify converting a Go function to buildable Go assembly
Labels: NeedsDecision,FeatureRequest,compiler/runtime

I'm slowly learning x86 assembly and how Go uses it, so it's very useful to see how existing functions in Go are translated to it.

The next logical step is to be able to make small changes to that assembly, to see if my understanding of it is correct, and to play with trying to make the code better manually. However, as I've found out, there's no easy way to move a function from a `.go` file to a `.s` file.

After reading some blog posts online and some trial and error, I've found out that most functions can be translated by hand. For example, take a single file with this very simple Go function:

```
package p

func Add(x, y int) int {
        return x + y
}
```

With `go tool compile -S f.go`, we can scroll through the output to find its assembly:

```
TEXT    "".Add(SB), NOSPLIT|ABIInternal, $0-24
FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
FUNCDATA        $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
PCDATA  $2, $0
PCDATA  $0, $0
MOVQ    "".y+16(SP), AX
MOVQ    "".x+8(SP), CX
ADDQ    CX, AX
MOVQ    AX, "".~r2+24(SP)
RET
```

However, we can't just add the assembly to the source code and make the Go func a stub, as that just fails:

```
$ go build
# foo.bar
./f.s:1: string constant must be an immediate
./f.s:7: string constant must be an immediate
./f.s:8: string constant must be an immediate
./f.s:10: string constant must be an immediate
asm: assembly of ./f.s failed
```

We can apply multiple changes to get it to work:

* remove `FUNCDATA` and `PCDATA` lines
* replace `"".Func` with `·Func`
* remove all addressing modes (?) like `NOSPLIT|ABIInternal`, to get rid of errors like `illegal or missing addressing mode for symbol NOSPLIT`
* replace `"".var` with `var`
* replace `"".~r2` with `r2` (I still don't understand that tilde)

End result, which works just as fine as the Go code:

```
TEXT    ·Add(SB),$0-24
MOVQ    y+16(SP), AX
MOVQ    x+8(SP), CX
ADDQ    CX, AX
MOVQ    AX, r2+24(SP)
RET
```

In this case, we're done. But if the function had jumps like `JMP 90`, we'd have to add a label like `L90` after finding the right line, and then modifying the jump to be `JMP L90`. `go vet` also tends to complain about the result of the process above, even when the assembly works - for example, in our case:

```
$ go vet
# foo.bar
./f.s:6:1: [amd64] Add: RET without writing to 8-byte ret+16(FP)
```

I've succsesfully done this with multiple funcs on the small side, but it's tedious. Worse even, I still haven't been able to correctly translate a function calling another function to assembly - I keep getting panics like `runtime: unexpected return pc for ...`, which are very confusing.

I think the compiler should support translating Go functions to assembly which can just be dumped onto a `.s` file after replacing their Go implementation with a stub. It doesn't matter if it couldn't directly work on some weird edge cases; if at least it could give a starting point after doing grunt work as mentioned above, that would already be a huge improvement.

Ideas that come to mind to improve `go tool compile -S` (thanks for @josharian for his input):

* Allow filtering for functions, e.g. via a regexp. Similar to `GOSSAFUNC`.
* Add a mode where the output can theoretically/probably be built inside a `.s` file as-is.

For example, borrowing from flags like `compile -d`, one might imagine `go tool compile -S=func=Add,clean` to do what I had done manually above.

If this makes sense, I'm happy to do some of the work during the upcoming cycle. I'll just need a bit of help from the compiler folks, as my assembly (and especially Go assembly syntax) knowledge is limited.

/cc @randall77 @griesemer @josharian @mdempsky @martisch, as per https://dev.golang.org/owners/.