Loading theme preference
Ilia Bukin
← Back to blog

llgo: a Go compiler that lowers C, Python, and JS into native calls

Sep 5, 2026

Calling C from Go has never been the smooth part of the language. Regular cgo works, but it costs: every call has to cross the Go-to-C stack and scheduler boundary, and the overhead adds up fast when you call out in a loop. A project called llgo is attacking that from a different angle. Instead of patching the official compiler, it builds a whole Go compiler on top of LLVM that talks to the C ecosystem through the C ABI directly. Console.dev featured it this week as the lead item in its weekly newsletter.

The headline claim from the project is that you get "FFI performance without FFI." Calls into C compile to native calls against the C ABI, so there is no Go-to-C stack transition and no scheduler handoff. Frequent C calls stay cheap instead of being the slow path.1

A Go compiler with a different runtime

llgo is a subproject of the XGo project, the language that reads like plain English and borrows assets from C/C++, Go, Python, and JavaScript. llgo is the compiler that makes the XGo side of that work: XGo currently defaults to the official go compiler, but plans to move to llgo as its default.2 The project describes the relationship in a short equation:

LLGo := Go * C ecosystem

llgo keeps Go compatibility at the source level and reaches into the C world through the C ABI. That includes languages that expose a C-compatible interface: C/C++, Python, JavaScript, Objective-C, and Swift.

To make direct calls possible, llgo ships its own runtime rather than the standard one. Native goroutines map 1:1 to OS threads with fixed native stacks, which is what lets a C call happen without the Go-to-C stack or scheduler transition that standard cgo needs.2

The cost of that design shows up in the garbage collector. The default collector is conservative Boehm-Demers-Weiser GC (libgc). Bare-metal embedded targets get a TinyGo-derived conservative mark-and-sweep collector instead. You can also disable collection entirely with the nogc build tag:

llgo run -tags nogc .

The interop trick: go:linkname against C symbols

Rather than invent wrapper machinery, llgo leans on go:linkname to bind a Go declaration directly to a C ABI symbol. This is from the project's own README:

import _ "unsafe" // for go:linkname

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

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

Because the call compiles to a native call against the C ABI, there is no Go-to-C stack or scheduler transition along the way. There are also Go bindings for a broad slice of the C and C++ standard libraries, packaged under the github.com/goplus/lib tree, so you can call c.Printf with a C string constant:

package main

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

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

The README points out that c.Str is not a runtime conversion from a Go string to a C string. It is a built-in instruction llgo recognizes and compiles directly into a C string constant.

Python, from Go

Python support is where this gets interesting. Through a tool called llpyg that converts Python libraries into Go packages, you can import Python bindings including py/numpy, py/pandas, and py/torch from Go, then call them like Go functions.1 A matrix-add example from the README:

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)

The third-party libraries still have to be installed separately; the bindings hand you the interface. The project's first AI example is inference on Llama 2.

Language coverage and targets

On the Go side, llgo says it supports the complete Go 1.27 language syntax plus cgo, and is compatible with Go 1.20+ source code. Compatibility is checked against the upstream GOROOT/test cases using a pinned Go 1.27 toolchain, with user packages tested against every Go 1.20 through 1.27 toolchain.1

For output, it covers more ground than the standard toolchain's mainstream targets:

  • Native Linux (amd64/arm64) and macOS (amd64/arm64), with releases for those.
  • WebAssembly, both js/wasm and wasip1/wasm. The -target emscripten flag emits an ES module plus a sibling wasm32 module, and -target wasi or -target wasip1 emits a WASI Preview 1 module.
  • Embedded, via -target configurations for supported boards and MCUs.

Where the tradeoffs sit

The design decisions are deliberate, which means they come with real costs. Goroutines that map 1:1 to OS threads are heavier than the lightweight goroutines the standard runtime schedules, and Console.dev flags that as the main thing to weigh.3 The conservative collector is also worth measuring against a standard build, since behavior can vary per target. Console.dev flags the collector as more conservative and different per target, and its advice is to benchmark against plain Go rather than assume a given workload wins.3

Beyond that, integration is still being built out. C and C++ standard library bindings exist, but LLGo's README notes that bindings beyond those are maintained by hand; only the Python import path is automated for now. The repo sits at just over 700 stars, and Windows support is still marked TODO in the install docs.

The pitch will remind some people of how TinyGo found a niche years ago: an alternative compiler that opens up target ecosystems the official toolchain does not serve well. llgo is younger and rougher around the edges, but the idea it rests on is concrete enough to test. If you have Go code where a hot loop keeps crossing a cgo boundary, running the same loop under llgo run and comparing is a short experiment.


  1. LLGo README, "LLGo is a Go compiler based on LLVM ... compatible with the C ecosystem through the C Application Binary Interface (ABI) ... A call into C compiles to native calls against the C ABI. no Go-to-C stack or scheduler transition." https://github.com/xgo-dev/llgo
  2. XGo README, "LLGo is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem ... Native goroutines map 1:1 to OS threads with fixed native stacks ... In the future, it will be [llgo]." https://github.com/goplus/xgo
  3. Console.dev review of llgo, 2026-09-03 newsletter. https://console.dev/