mmcloughlin / avo
- четверг, 10 января 2019 г. в 00:16:52
Go
Generate x86 Assembly with Go
Generate x86 Assembly with Go
avo makes high-performance Go assembly easier to write, review and maintain. The avo package presents a familiar assembly-like interface that simplifies development without sacrificing performance:
avo programs are Go programsavo assigns physical registers for youNote: APIs subject to change while avo is still in an experimental phase. You can use it to build real things but we suggest you pin a version with your package manager of choice.
Install avo with go get:
$ go get -u github.com/mmcloughlin/avo
avo assembly generators are pure Go programs. Here's a function that adds two uint64 values:
// +build ignore
package main
import (
. "github.com/mmcloughlin/avo/build"
)
func main() {
TEXT("Add", NOSPLIT, "func(x, y uint64) uint64")
Doc("Add adds x and y.")
x := Load(Param("x"), GP64())
y := Load(Param("y"), GP64())
ADDQ(x, y)
Store(y, ReturnIndex(0))
RET()
Generate()
}go run this code to see the assembly output. To integrate this into the rest of your Go package we recommend a go:generate line to produce the assembly and the corresponding Go stub file.
//go:generate go run asm.go -out add.s -stubs stub.goAfter running go generate the add.s file will contain the Go assembly.
// Code generated by command: go run asm.go -out add.s -stubs stub.go. DO NOT EDIT.
#include "textflag.h"
// func Add(x uint64, y uint64) uint64
TEXT ·Add(SB), NOSPLIT, $0-24
MOVQ x(FP), AX
MOVQ y+8(FP), CX
ADDQ AX, CX
MOVQ CX, ret+16(FP)
RETThe same call will produce the stub file stub.go which will enable the function to be called from your Go code.
// Code generated by command: go run asm.go -out add.s -stubs stub.go. DO NOT EDIT.
package add
// Add adds x and y.
func Add(x uint64, y uint64) uint64See the examples/add directory for the complete working example.
See examples for the full suite of examples.
Sum a slice of uint64s:
func main() {
TEXT("Sum", NOSPLIT, "func(xs []uint64) uint64")
Doc("Sum returns the sum of the elements in xs.")
ptr := Load(Param("xs").Base(), GP64())
n := Load(Param("xs").Len(), GP64())
// Initialize sum register to zero.
s := GP64()
XORQ(s, s)
// Loop until zero bytes remain.
Label("loop")
CMPQ(n, Imm(0))
JE(LabelRef("done"))
// Load from pointer and add to running sum.
ADDQ(Mem{Base: ptr}, s)
// Advance pointer, decrement byte count.
ADDQ(Imm(8), ptr)
DECQ(n)
JMP(LabelRef("loop"))
// Store sum to return value.
Label("done")
Store(s, ReturnIndex(0))
RET()
Generate()
}The result from this code generator is:
// Code generated by command: go run asm.go -out sum.s -stubs stub.go. DO NOT EDIT.
#include "textflag.h"
// func Sum(xs []uint64) uint64
TEXT ·Sum(SB), NOSPLIT, $0-32
MOVQ xs_base(FP), AX
MOVQ xs_len+8(FP), CX
XORQ DX, DX
loop:
CMPQ CX, $0x00
JE done
ADDQ (AX), DX
ADDQ $0x08, AX
DECQ CX
JMP loop
done:
MOVQ DX, ret+24(FP)
RETFull example at examples/sum.
For demonstrations of avo features:
DATA sections.complex{64,128} types.Implementations of full algorithms:
StadtX hash port from dgryski/go-stadtx.Contributions to avo are welcome:
avo in a real project is incredibly valuable. Consider porting an existing project to avo.Inspired by the PeachPy and asmjit projects. Thanks to Damian Gryski for advice, and his extensive library of PeachPy Go projects.
avo is available under the BSD 3-Clause License.