Skip to content

gorse-io/goat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GoAT

test GitHub release (with filter)

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

Prerequisites

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

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

Usage

  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

Example

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)))
}

Limitations

  • No call statements except for inline functions.
  • Arguments must be int64_t, long, float, double, _Bool or pointer.
  • Potentially BUGGY code generation.

Acknowledgments

About

Go assembly transpiler for C programming language

Resources

License

Stars

Watchers

Forks

Packages

No packages published