Go assembly transpiler for C programming languages. It help to utilize optimization from C compiler in Go projects. For example, generate SIMD vectorized functions for Go (refer to How to Use AVX512 in Golang).
The following packages are required:
- Ubuntu for AMD64:
sudo apt update
sudo apt install clang libc6-dev-i386
- Ubuntu for ARM64:
sudo apt update
sudo apt install clang
- macOS:
brew install llvm binutils
# Override the default clang and objdump to use Homebrew's version
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
export PATH="/opt/homebrew/opt/binutils/bin:$PATH"
- Windows:
Install LLVM and MinGW from the official website or use Chocolatey package manager:
choco install llvm mingw
Cross compile is not supported, so you need to run GoAT on the same architecture as the target architecture.
Install the latest version:
go install github.com/gorse-io/goat@latest
Or install the latest development version:
go install github.com/gorse-io/goat@main
goat source [-o output_directory] [flags]
Flags:
-e, --extra-option strings extra option for clang
-h, --help help for goat
-m, --machine-option strings machine option for clang
-O, --optimize-level int optimization level for clang
-o, --output string output directory of generated files
-v, --verbose if set, increase verbosity level
Suppose you have a C function that adds two arrays of floats in src/add.c
:
void add(float *a, float *b, float *result, long n) {
for (long i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
}
You can use GoAT to transpile this C function to Go assembly code with the following command:
goat src/add.c -o . -O3 -mavx
Warning
The output directory must be different from the source directory, otherwise the intermediate files will break the compilation.
Two files will be generated in the output directory:
- Go function definition file
add.go
:
//go:build !noasm && amd64
// Code generated by GoAT. DO NOT EDIT.
// versions:
// clang 18.1.3 (1ubuntu1)
// objdump 2.42
// flags: -mavx -O0
// source: example/src/add.c
package example
import "unsafe"
//go:noescape
func add(a, b, result unsafe.Pointer, n int64)
- Go assembly file
add.s
:
//go:build !noasm && amd64
// Code generated by GoAT. DO NOT EDIT.
// versions:
// clang 18.1.3 (1ubuntu1)
// objdump 2.42
// flags: -mavx -O0
// source: example/src/add.c
TEXT ·add(SB), $0-32
MOVQ a+0(FP), DI
MOVQ b+8(FP), SI
MOVQ result+16(FP), DX
MOVQ n+24(FP), CX
BYTE $0x55 // pushq %rbp
WORD $0x8948; BYTE $0xe5 // movq %rsp, %rbp
LONG $0xf8e48348 // andq $-8, %rsp
LONG $0x30ec8348 // subq $48, %rsp
LONG $0x247c8948; BYTE $0x28 // movq %rdi, 40(%rsp)
LONG $0x24748948; BYTE $0x20 // movq %rsi, 32(%rsp)
LONG $0x24548948; BYTE $0x18 // movq %rdx, 24(%rsp)
LONG $0x244c8948; BYTE $0x10 // movq %rcx, 16(%rsp)
QUAD $0x000000082444c748; BYTE $0x00 // movq $0, 8(%rsp)
LBB0_1:
LONG $0x24448b48; BYTE $0x08 // movq 8(%rsp), %rax
LONG $0x24443b48; BYTE $0x10 // cmpq 16(%rsp), %rax
JGE LBB0_4
LONG $0x24448b48; BYTE $0x28 // movq 40(%rsp), %rax
LONG $0x244c8b48; BYTE $0x08 // movq 8(%rsp), %rcx
LONG $0x0410fac5; BYTE $0x88 // vmovss (%rax,%rcx,4), %xmm0 # xmm0 = mem[0],zero,zero,zero
LONG $0x24448b48; BYTE $0x20 // movq 32(%rsp), %rax
LONG $0x244c8b48; BYTE $0x08 // movq 8(%rsp), %rcx
LONG $0x0458fac5; BYTE $0x88 // vaddss (%rax,%rcx,4), %xmm0, %xmm0
LONG $0x24448b48; BYTE $0x18 // movq 24(%rsp), %rax
LONG $0x244c8b48; BYTE $0x08 // movq 8(%rsp), %rcx
LONG $0x0411fac5; BYTE $0x88 // vmovss %xmm0, (%rax,%rcx,4)
LONG $0x24448b48; BYTE $0x08 // movq 8(%rsp), %rax
LONG $0x01c08348 // addq $1, %rax
LONG $0x24448948; BYTE $0x08 // movq %rax, 8(%rsp)
JMP LBB0_1
LBB0_4:
WORD $0x8948; BYTE $0xec // movq %rbp, %rsp
BYTE $0x5d // popq %rbp
RET
Finally, the add
function can be used by:
func Add(a, b, c []float32) {
if len(a) ! = len(b) || len(a) ! = len(c) {
panic("floats: slice lengths do not match")
}
add(unsafe.Pointer(&a[0]), unsafe.Pointer(&b[0]), unsafe.Pointer(&c[0]), int64(len(a)))
}
- No call statements except for inline functions.
- Arguments must be
int64_t
,long
,float
,double
,_Bool
or pointer. - Potentially BUGGY code generation.
- GoAT is inspired by c2goasm.