Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: simplify converting a Go function to buildable Go assembly #29538

Open
mvdan opened this issue Jan 3, 2019 · 3 comments
Labels

Comments

@mvdan
Copy link
Member

@mvdan mvdan commented Jan 3, 2019

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/.

@mvdan mvdan added the NeedsDecision label Jan 3, 2019
@mvdan

This comment has been minimized.

Copy link
Member Author

@mvdan mvdan commented Jan 3, 2019

Someone on Slack mentioned https://github.com/rsc/tmp/tree/master/go2asm, which seems to be a very similar tool. Looks a bit outdated and seems like it only supports amd64. I wonder if optimistically supporting all architectures would be a good idea. /cc @rsc

@josharian

This comment has been minimized.

Copy link
Contributor

@josharian josharian commented Jan 4, 2019

Allow filtering for functions,

This is something I want frequently.

@mvdan

This comment has been minimized.

Copy link
Member Author

@mvdan mvdan commented Jan 4, 2019

I forgot to address one point - why this should be part of go tool compile. I personally think this is something at the same level of usefulness as go tool compile -S, if not more. But I'd be fine with it living in a repo like x/tools instead, as long as it's still officially maintained and easy to find.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.