English static mirror for SEO/GEO · AI-assisted translation · Read Chinese original

Parasitic JIT for Go: Accelerating Code Without Fighting the Runtime

Forum topic · 小凯 · 2026-04-16

Summary

A Go forum post explains a 'parasitic' architecture for adding JIT-like acceleration to Go programs without modifying or triggering the Go runtime. Because the runtime validates every PC address it sees, foreign machine code causes fatal 'unknown pc' errors. The proposed solution uses three components: eBPF-based kernel-side tracing to find hot code paths with near-zero overhead; compiling hot paths into WebAssembly executed by the wazero sandbox engine; and a Go assembly 'trampoline' with an ABI0 declaration acting as a legitimate entry point so the runtime only sees a standard Go function. Critical safety rules include keeping JIT code pointer-free so the GC cannot collect referenced objects, spilling registers to C-heap memory (malloc) rather than Go stacks, and bailing out immediately on uncovered instructions. The post frames the design philosophy as gaining JIT benefits without making Go support JIT, with Wasm as the firewall isolating the JIT from the runtime.

The Problem: Go's Runtime Is a "Control Freak"

To speed up Go, the natural idea is a JIT: compile hot code into machine code and execute it directly. This works in Java or C++, but breaks in Go.

The Go runtime tracks everything — stack pointers, stack growth, GC timing. If you inject your own generated machine code, the runtime sees an unrecognized PC address and throws fatal error: unknown pc, killing the process.

It's like smuggling a hot plate into a strictly managed dorm: the supervisor checks every hour, finds an unfamiliar device, and cuts the power.

The answer: instead of fighting the runtime head-on, become a parasite.

The Three-Step Approach

Step 1: Watch (Tracing)

Don't instrument Go code — that's slow and detectable. Use eBPF to profile from the kernel side. Observe from outside the window while the supervisor checks the room: record the most frequent behaviors at near-zero overhead, completely undetected.

Step 2: Write the Script (Compilation)

Once hot paths are identified, don't generate raw machine code. Instead, rewrite the hot behavior as a "script" in WebAssembly (Wasm).

Wasm is a sandbox. The runtime can't read the machine code inside — and doesn't need to. The Wasm engine (wazero) has already handled all safety concerns. The supervisor just sees "a legitimate student reading a book," unaware of what's written in it.

Step 3: Swap the Actor (Execution)

To execute the script, use a "legitimate student": a trampoline written in Go assembly, carrying an ABI0 declaration as its "student ID."

It raises its hand in class ("I'll answer"), the runtime approves, it secretly reads the script, then raises its hand again ("done"). The runtime only ever sees a legitimate Go assembly function.

The Core Insight

The goal is not "how to make Go support JIT" but "how to get JIT benefits while Go doesn't support JIT."

  • Wasm is the firewall isolating the runtime from the JIT
  • eBPF is the periscope, observing without being detected
  • The trampoline is the springboard, legitimately entering the isolated zone
  • Risks and Rules

    Even with isolation, strict rules apply:

  • JIT code must be pointer-free — the GC can't see JIT-internal pointers; if one references a Go object, the GC may collect it, causing use-after-free crashes.
  • Spill to the C heap — when registers run out, spill data to malloc'd C memory, never to Go stacks (the GC scans stacks).
  • Bail out immediately on uncovered instructions — don't push through an instruction that might trigger a runtime checkpoint.
  • Technical Details

  • eBPF tracing: kernel-side non-intrusive profiling, near-zero overhead
  • Wasm sandbox: wazero engine solves cross-platform/safety/performance; drafter is only ~2B parameter scale
  • Trampoline: written in Go assembly with an ABI0 declaration, a legitimate anchor point
  • Isolation policy: JIT code must be pointer-free; register spills go to the C heap (malloc)
  • Safety anchors: all JIT calls are wrapped in standard Go assembly functions

The Feynman Check

Explain it to a freshman: you want to secretly cook hotpot in a strictly supervised dorm.

1. Install a camera outside the window (eBPF) to see when roommates are hungriest; 2. Write the cooking steps as a script (Wasm) in a language the supervisor can't read; 3. Have a legitimate roommate (trampoline) with a student ID cook per the script while the supervisor isn't looking.

The supervisor only sees a normal roommate the whole time.

This is the parasitic architecture: not "turn Go into Java," but "find the optimal solution within Go's constraints." Avoid the strength, don't clash head-on.

---

References: Go Runtime ABIInternal documentation, eBPF kernel documentation, wazero project.

Tags

#go#jit#webassembly#wazero#ebpf#runtime#profiling#performance

This page is an English static mirror generated for search and AI citation. It may be a full translation or structured summary of the Chinese original. Canonical interactive discussion lives on the Chinese page: https://zhichai.net/topic/177618504