cd /news/developer-tools/llgo-go-compiler-based-on-llvm-that-… · home topics developer-tools article
[ARTICLE · art-114882] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

LLGo: Go compiler based on LLVM that integrate Go with the C ecosystem

LLGo, a Go compiler based on LLVM from the XGo project, aims to integrate Go with the C ecosystem by supporting Go 1.20+ source code and the complete Go 1.26 language syntax, including cgo. It uses a different runtime with native goroutines mapped 1:1 to OS threads and a conservative BDWGC garbage collector, enabling direct C calls without cgo overhead. The compiler supports native Linux and macOS targets, as well as js/wasm and wasip1/wasm builds, and allows importing C/C++ libraries directly via go:linkname bindings.

read8 min views1 publishedAug 29, 2026
LLGo: Go compiler based on LLVM that integrate Go with the C ecosystem
Image: Michielbdejong (auto-discovered)

LLGo is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem, including Python and JavaScript. It's a subproject of the XGo project.

LLGo aims to expand the boundaries of Go/XGo, providing limitless possibilities such as:

  • Game development
  • AI and data science
  • WebAssembly
  • Embedded development
  • ...

How can these be achieved?

LLGo := Go * C ecosystem

LLGo is compatible with the C ecosystem through the C Application Binary Interface (ABI), while LLGo is compatible with Go at the source-code level. The C ecosystem includes languages that expose C-compatible interfaces (e.g. C/C++, Python, JavaScript, Objective-C, and Swift).

LLGo is compatible with Go 1.20+ source code and supports the complete Go 1.26 language syntax, as well as cgo

.

Compatibility is checked against applicable upstream GOROOT/test cases using pinned Go 1.25 and Go 1.26 toolchains. Remaining applicable differences are recorded in

; gc-specific mechanisms outside LLGo's compatibility goals are documented in

xfail.yaml

.

notapplicable.yaml

LLGo uses a different runtime from the standard Go toolchain. Native goroutines map 1:1 to OS threads with fixed native stacks, so direct C calls require no Go-to-C stack or scheduler transition, avoiding the cgo overhead that makes frequent C calls costly in standard Go.

The default garbage collector is conservative BDWGC (also known as libgc). Bare-metal embedded targets instead use a TinyGo-derived conservative mark-and-sweep collector.

Garbage collection can be disabled with the nogc

build tag. For example:

llgo run -tags nogc .

LLGo fully supports the Go standard library on supported native platforms. CI requires compatibility coverage for every public package and exported symbol in the primary Go toolchain, and runs test/std with both supported toolchains.

Other targets may not provide every OS service or implementation-specific runtime behavior.

Target Current coverage
Native Linux amd64/arm64 and macOS amd64/arm64

js/wasm

and wasip1/wasm

builds; WASI and Emscripten CI coverageconfigurations for supported boards and MCUs, with selected QEMU/emulator smoke tests-target

LLGo lets you import and call C/C++ libraries directly, without wrappers or cgo overhead.

LLGo uses go:linkname

to bind a Go declaration directly to a C ABI symbol:

import _ "unsafe" // for go:linkname

//go:linkname Sqrt C.sqrt
func Sqrt(x float64) float64

You can use this directly in your own code:

package main

import _ "unsafe" // for go:linkname

//go:linkname Sqrt C.sqrt
func Sqrt(x float64) float64

func main() {
	println("sqrt(2) =", Sqrt(2))
}

Or organize such bindings into a package, as c/math does:

package main

import "github.com/goplus/lib/c/math"

func main() {
	println("sqrt(2) =", math.Sqrt(2))
}

Because calls into C compile to native calls against the C ABI, there is no Go-to-C stack or scheduler transition, so frequent C calls stay cheap.

On Windows, bind APIs declared with WINAPI

or __stdcall

through the stdcall.

namespace. The convention is distinct on 386; Windows amd64 and arm64 use their unified native C ABI. An explicitly decorated 386 name such as _MessageBoxW@16

is also accepted and is normalized to MessageBoxW

on 64-bit targets.

//go:linkname MessageBoxW stdcall.MessageBoxW
func MessageBoxW(hwnd uintptr, text, caption *uint16, flags uint32) int32

//llgo:type stdcall
type Callback func(context uintptr) uintptr

stdcall.

declarations and //llgo:type stdcall

apply only to non-variadic function types. A native callback is one function pointer, so a Go callback must be a direct function reference; pass state through an explicit context pointer rather than a capturing closure.

LLGo provides Go bindings for the C/C++ standard library:

Package Description

c/syscallc/sysc/osc/mathc/math/cmplxc/math/randc/pthreadc/pthread/syncc/sync/atomicc/timec/netcpp/stdHere is a simple example calling the C printf

function:

package main

import "github.com/goplus/lib/c"

func main() {
	c.Printf(c.Str("Hello world\n"))
}

c.Str

is not a runtime conversion from a Go string to a C string — it is a built-in instruction that llgo

recognizes and compiles directly into a C string constant.

Additional demos are available in the _demo

directory (prefixed with _

so the go

command skips them):

hello: call Cprintf

to printHello world

concat: call Cfprintf

withstderr

qsort: call a C function that takes a callback (e.g.qsort

)

To run a demo (see How to install if llgo

isn't installed yet):

cd <demo-directory>  # e.g. cd _demo/c/hello
llgo run .

Beyond the standard library, LLGo can import libraries from across the C/C++ ecosystem. Bindings are currently maintained by hand; automating this process, as is already done for Python library imports, is planned for the future.

Available bindings include:

c/bdwgcc/cjsonc/clangc/ffic/libuvc/llama2c/luac/necoc/opensslc/raylibc/sqlitec/zlibcpp/inihcpp/llvm

Examples built on these bindings:

llama2-c: inference Llama 2 (the first LLGo AI example)mkjson: create a JSON object and print itsqlitedemo: a basic SQLite demotetris: a Tetris game based on raylib

You can import a Python library in LLGo!

You can import Python libraries into llgo

through llpyg

(see Development tools). Available bindings include:

py(abi)py/std(builtins)py/syspy/ospy/mathpy/jsonpy/inspectpy/statisticspy/numpypy/pandaspy/torchpy/matplotlib

Third-party libraries such as pandas and PyTorch must be installed separately.

Here is an example:

package main

import (
	"github.com/goplus/lib/py"
	"github.com/goplus/lib/py/math"
	"github.com/goplus/lib/py/std"
)

func main() {
	x := math.Sqrt(py.Float(2))       // x = sqrt(2)
	std.Print(py.Str("sqrt(2) ="), x) // print("sqrt(2) =", x)
}

It is equivalent to the following Python code:

import math

x = math.sqrt(2)
print("sqrt =", x)

Here, We call py.Float(2)

to create a Python number 2, and pass it to Python’s math.sqrt

to get x

. Then we call std.Print

to print the result.

Let's look at a slightly more complex example. For example, we use numpy

to calculate:

package main

import (
	"github.com/goplus/lib/py"
	"github.com/goplus/lib/py/numpy"
	"github.com/goplus/lib/py/std"
)

func main() {
	a := py.List(
		py.List(1.0, 2.0, 3.0),
		py.List(4.0, 5.0, 6.0),
		py.List(7.0, 8.0, 9.0),
	)
	b := py.List(
		py.List(9.0, 8.0, 7.0),
		py.List(6.0, 5.0, 4.0),
		py.List(3.0, 2.0, 1.0),
	)
	x := numpy.Add(a, b)
	std.Print(py.Str("a+b ="), x)
}

Here we define two 3x3 matrices a and b, add them to get x, and then print the result.

The _demo/py/

directory contains some python related demos:

callpy: call Python standard library functionmath.sqrt

pi: print python constantsmath.pi

statistics: define a python list and callstatistics.mean

to get the meanmatrix: a basicnumpy

demo

To run these demos (If you haven't installed llgo

yet, please refer to How to install):

cd <demo-directory>  # eg. cd _demo/py/callpy
llgo run .

Go 1.25+(to build LLGo; CI also validates user packages with pinned Go 1.25 and Go 1.26 toolchains)LLVM 19Clang 19LLD 19pkg-config 0.29+bdwgc/libgc 8.0+libffilibuvOpenSSL 3.0+zlib 1.2+Python 3.12+(optional, forgithub.com/goplus/lib/py)

Follow these steps to install the llgo

command, whose usage is similar to the go

command:

brew update
brew install llvm@19 lld@19 bdw-gc openssl cjson libffi libuv pkg-config
brew install python@3.12 # optional
brew link --overwrite llvm@19 lld@19 libffi
./install.sh
echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-19 main" | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y llvm-19-dev clang-19 libclang-19-dev lld-19 libunwind-19-dev libc++-19-dev pkg-config libgc-dev libssl-dev zlib1g-dev libffi-dev libcjson-dev libsqlite3-dev libuv1-dev
sudo apt-get install -y python3.12-dev # optional
#curl https://raw.githubusercontent.com/xgo-dev/llgo/refs/heads/main/install.sh | bash
./install.sh
apk add go llvm19-dev clang19-dev lld19 pkgconf gc-dev libunwind-dev openssl-dev zlib-dev
apk add python3-dev # optional
apk add g++ # build only
export LLVM_CONFIG=/usr/lib/llvm19/bin/llvm-config
export CGO_CPPFLAGS="$($LLVM_CONFIG --cppflags)"
export CGO_CXXFLAGS=-std=c++17
export CGO_LDFLAGS="$($LLVM_CONFIG --ldflags) $($LLVM_CONFIG --libs all)"
curl https://raw.githubusercontent.com/xgo-dev/llgo/refs/heads/main/install.sh | bash

docker alpine 386 llgo environment

export GCC_ROOT_DIR=$(gcc -print-search-dirs | grep 'install:' | awk -F': ' '{print $2}')
export LDFLAGS="-L$GCC_ROOT_DIR -B$GCC_ROOT_DIR -Wl,-dynamic-linker,/lib/ld-musl-i386.so.1"
llgo run .

TODO

git clone https://github.com/xgo-dev/llgo.git
cd llgo
./install.sh

pydump: It is the first production program compiled withllgo

rather thango

. It outputs symbol information (functions, variables, and constants) from a Python library in JSON format, preparing for the generation of corresponding packages inllgo

.pysigfetch: It generates symbol information by extracting information from Python's documentation site. This tool is not part of thellgo

project, but we depend on it.llpyg: It is used to automatically convert Python libraries into Go packages thatllgo

can import. It depends onpydump

andpysigfetch

to accomplish the task.llgen: It is used to compile Go packages into LLVM IR files (*.ll).gentests: It refreshes runtime-output and package-metadata golden data undercl/_test*

. LLVM IR checks live in Go sources as// LITTEST

FileCheck directives.litgen: It maintains explicitly opted-in, source-embedded FileCheck snapshots. It supports function/global selection, update-only operation, stale-check verification, and stable LLVM value abstractions. Small handwritten checks remain manual.ssadump: It is a Go SSA builder and interpreter.

For local workflows and test-golden refresh commands, see dev/README.md.

How do I generate these tools?

git clone https://github.com/xgo-dev/llgo.git
cd llgo
go install -v ./cmd/...
go install -v ./chore/...  # compile all tools except pydump
export LLGO_ROOT=$PWD
cd _xtool
llgo install ./...   # compile pydump
go install github.com/goplus/hdq/chore/pysigfetch@v0.8.1  # compile pysigfetch

Below are the key modules for understanding the implementation principles of llgo

:

ssa: It generates LLVM IR files (LLVM SSA) using the semantics and interfaces of Go SSA. AlthoughLLVM SSA

andGo SSA

are both IR languages, they work at completely different levels.LLVM SSA

is closer to machine code and abstracts over different instruction sets, whileGo SSA

is closer to a high-level language. We can think of it as the instruction set of theGo computer

.llgo/ssa

is not limited to thellgo

compiler. If we view it as providing the high-level expressive power ofLLVM

, it is very useful. Its advanced SSA form lets clients use LLVM without operating directly on machine-code semantics.cl: It is the core of the llgo compiler. It converts a Go package into LLVM IR files. It depends onllgo/ssa

.internal/build: It strings together the entire compilation process ofllgo

. It depends onllgo/ssa

andllgo/cl

.

── more in #developer-tools 4 stories · sorted by recency
── more on @llgo 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/llgo-go-compiler-bas…] indexed:0 read:8min 2026-08-29 ·