Open-source project
xgo-dev/llgo avatar
xgo-dev/llgo

LLGo compiles Go through LLVM to call C without cgo

A Go compiler based on LLVM in order to better integrate Go with the C ecosystem including Python and JavaScript

845 stars55 forksGoApache-2.0

At a glance

What is it?
LLGo is a Go compiler built on LLVM, a subproject of the XGo project, that binds Go declarations to C ABI symbols with go:linkname and uses its own runtime so that frequent C calls avoid the cgo transition. It suits people who need Go to interoperate with C, C++, Python or JavaScript without wrappers.
Who is it for?
Adopt LLGo when the cost of cgo in a hot path is the problem, since calls into C compile to native calls with no Go to C stack or scheduler transition, and the project targets game development, AI and data science, WebAssembly and embedded work. Do not adopt it as a drop-in replacement for the standard toolchain, because it uses a different runtime, a conservative collector and its own compatibility scope, and some Go specific mechanisms fall outside its goals.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Go compiler that targets LLVM instead of gc

The project description says it plainly: LLGo is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem, including Python and JavaScript. It is a subproject of the XGo project, and the README gives the relationship as a formula, `LLGo := Go * C ecosystem`. The compatibility claim is asymmetric on purpose. LLGo is compatible with the C ecosystem through the C Application Binary Interface, and compatible with Go at the source code level. The C ecosystem is defined as languages that expose C compatible interfaces, which the README names as C and C++, Python, JavaScript, Objective-C and Swift. The stated ambition is to expand the boundaries of Go and XGo, with game development, AI and data science, WebAssembly and embedded development named as the areas this would open.

go:linkname is the whole interop trick

The mechanism is remarkably small. LLGo uses `go:linkname` to bind a Go declaration directly to a C ABI symbol, and the README shows the shape of a binding:

go
import _ "unsafe" // for go:linkname

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

A complete program using it looks like this:

go
package main

import _ "unsafe" // for go:linkname

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

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

The same pattern can be organised into a package, which the README points at with the `c/math` package of the goplus lib repository. The result is that there is no wrapper layer and no cgo overhead, and the performance argument follows from the ABI: 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.

Its own runtime, with goroutines on OS threads

The performance claim depends on a runtime that the project supplies itself. The README says LLGo uses a different runtime from the standard Go toolchain, and explains the design: native goroutines map one to one to OS threads with fixed native stacks, so direct C calls need no Go to C stack or scheduler transition, which avoids the cgo overhead that makes frequent C calls costly in standard Go. Memory management is also different. The default garbage collector is the conservative BDWGC collector, also known as libgc, while bare metal embedded targets use a TinyGo derived conservative mark and sweep collector instead. Collection can be turned off with the `nogc` build tag:

sh
llgo run -tags nogc .

That is a meaningful difference for a project with a latency budget, and it also means the runtime behaviour of a program built with LLGo is not the behaviour you get from gc.

WebAssembly targets and the stack size flag

The target table in the README is the clearest picture of the platform scope. Native covers Linux, macOS and Windows on amd64 and arm64, with release builds for MSVC and MinGW on Windows. WebAssembly covers `js/wasm` and `wasip1/wasm` builds with WASI and Emscripten coverage in CI, and the supported WebAssembly profiles combine a memory ABI with a host ABI. Concretely, `-target emscripten` emits a Memory32 ES module and its sibling wasm module, `-target emscripten-memory64` emits the Memory64 pair, and `-target wasi` emits a Memory32 WASI Preview 1 module, while the raw `GOOS=js GOARCH=wasm` and `GOOS=wasip1 GOARCH=wasm` environment settings select the Go compatible JavaScript and WASI host contracts. There is one wasm specific flag to know. WebAssembly goroutine stacks have fixed sizes, so for deeply recursive workloads you set `-goroutine-stack-size=2MB` at build time, which sets both the goroutine stack and its Asyncify buffer but not the main goroutine's stack.

Compatibility is checked against the upstream test suite

The project treats Go compatibility as a measured property rather than a claim. The README says compiler compatibility is checked against applicable upstream `GOROOT/test` cases using the pinned Go 1.27 toolchain, and that user projects and packages under `test/` are additionally tested with exact Go 1.20 through Go 1.27 toolchains. The differences are then written down rather than hidden. Remaining applicable differences are recorded in `xfail.yaml` under `test/goroot`, and gc specific mechanisms that fall outside LLGo's compatibility goals are documented in `notapplicable.yaml`. On the language side, LLGo is compatible with Go 1.20 and later source code, supports the complete Go 1.27 language syntax, and supports `cgo`. For the standard library, CI requires compatibility coverage for every public package and exported symbol in the primary Go toolchain, with focused `test/std` compatibility sets run with each older supported toolchain, while other targets may not provide every OS service or implementation specific runtime behavior.

Where the C variadic support stops

This is the sharpest limitation documented, and it is worth reading carefully if you bind C++ libraries. Forwarding an incoming C ellipsis through a promoted method wrapper, including through an interface call, is currently supported only on hosted native targets without an explicit `-target` profile. The underlying C method must have a pointer receiver, and the wrapper and the callee must have matching fixed LLVM prototypes and calling conventions with no result conversion, and fixed parameters must be scalars with zero or one scalar result, where booleans, integers, floating point values and pointers qualify. Everything else is out: value receivers, aggregate fixed parameters or results, multiple results, WebAssembly, and explicit `-target` profiles including custom native ones. The warning that follows is the important part. Their existing wrapper lowering can silently drop the incoming ellipsis, and a promoted value receiver method called through an interface still loses arguments at O0. Valid LLVM IR for an excluded wrapper does not establish correct argument forwarding, and the work is tracked in issue 2855.

Building it with Docker and the release tooling

The repository is set up for reproducible builds. The Dockerfile pins its base image as `golang:1.27.0-bookworm`, and GoReleaser supplies exactly one complete DEB per target platform, which is extracted so that Go comes from the pinned base image rather than from Debian. The native dependencies are installed with apt-get in the same step, and the container then puts the cross-compile clang on the PATH and ends with `llgo version` as its default command:

dockerfile
FROM ${BASE_IMAGE}
ENV PATH="/usr/lib/llgo/crosscompile/clang/bin:${PATH}"
WORKDIR /workspace
CMD ["llgo", "version"]

The root of the repository shows the shape of the compiler itself. There is `ssa/` for the static single assignment layer, `targets/` for the target profiles, `runtime/` and `cl/` for the runtime and the C layer, `ltoplugin/` for the LLVM plugin, `internal/`, `cmd/`, `xtool/` and `_xtool/` for the tooling, `benchmark/` for benchmarks, `test/` for the test suites, `doc/` for the documentation including the embedded and wasm proposals, and `dev/` and `chore/` for repository automation. Install scripts exist for both Unix and Windows with `install.sh` and `install.ps1`, and a separate `WINDOWS.md` covers the Windows specifics, while `Dockerfile` and `.goreleaser.yaml` drive the packaged builds.

Apache-2.0 with third-party notices, and a young 1.0 line

The licence is Apache-2.0, which in short terms allows use, modification and redistribution including commercially, with the notice preserved and the licence's patent and contribution provisions applying. The repository also carries a `LICENSES/` directory and a `THIRD_PARTY_NOTICES.md`, which is what you would expect from a compiler that links against LLVM and other third party code, and the notices matter for anyone shipping binaries. The project has only recently reached a stable line. The listed releases are v1.0.7 on 2026-10-19, v1.0.6 on 2026-09-28 and v1.0.5 on 2026-09-28, and the last push was on 2026-10-20, so the repository is not archived and work is current. The module path is `github.com/xgo-dev/llgo`, it declares `go 1.27.0`, and the `go.mod` carries two `replace` directives, one pinning the runtime to the local `./runtime` directory and another pinning `golang.org/x/tools` to a fork until upstream includes a generic method wrapper fix.

Editorial conclusion

Adopt LLGo when the cost of cgo in a hot path is the problem, since calls into C compile to native calls with no Go to C stack or scheduler transition, and the project targets game development, AI and data science, WebAssembly and embedded work. Do not adopt it as a drop-in replacement for the standard toolchain, because it uses a different runtime, a conservative collector and its own compatibility scope, and some Go specific mechanisms fall outside its goals. Verify first which of your packages rely on cgo, since the README lists cgo as supported but not as free, and check the `notapplicable.yaml` file in `test/goroot` for the mechanisms that are explicitly out of scope.

Frequently asked questions

How does LLGo call C functions from Go?

It uses `go:linkname` to bind a Go declaration directly to a C ABI symbol, for example binding `Sqrt` to `C.sqrt`. Calls into C compile to native calls against the C ABI, so there is no Go to C stack or scheduler transition.

Which Go versions does LLGo support?

It is compatible with Go 1.20 and later source code, supports the complete Go 1.27 language syntax, and supports `cgo`. Compatibility is checked against the upstream GOROOT/test cases with the pinned Go 1.27 toolchain and with exact Go 1.20 to 1.27 toolchains.

Which garbage collector does LLGo use?

The default is the conservative BDWGC collector, also known as libgc, while bare metal embedded targets 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 .`.

What are the limits of C variadic method support in LLGo?

Forwarding a C ellipsis through a promoted method wrapper is supported only on hosted native targets without an explicit `-target` profile, with a pointer receiver and matching fixed LLVM prototypes. Value receivers, aggregates, multiple results, WebAssembly and explicit target profiles are excluded, and their lowering can silently drop arguments, tracked in issue 2855.

Which platforms can LLGo build for?

Native builds cover Linux, macOS and Windows on amd64 and arm64, with MSVC and MinGW on Windows. WebAssembly covers `js/wasm` and `wasip1/wasm` plus Emscripten and WASI profiles, and embedded targets use `-target` configurations for supported boards and MCUs.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. xgo-dev/llgo on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xgo-dev-llgo.svg)](https://hysenlabs.com/projects/xgo-dev-llgo)

Was this useful?