TinyGo Compiler

repository·dev·Indexed 12 days ago

https://github.com/tinygo-org/tinygo

A Go compiler designed for resource-constrained environments, including microcontrollers, WebAssembly (WASM/WASI), and small command-line tools. It leverages LLVM to produce small, efficient binaries and features a partial evaluator to convert runtime initialization code into static global data to reduce memory consumption. Supports flashing to over 150 microcontroller boards and compiling for browser-based WASM and server-side WASI runtimes.

Tokens
12.4K
Snippets
50
Records
66
Agent score
91%

What's inside TinyGo

  1. Benefits of partial evaluation for initialization code

    dev

    Partial evaluation is used to transform initialization functions into constant globals. This is necessary because:

    • LLVM Optimization: LLVM optimizes globals with initializers much more effectively than initialization code.
    • Dead Code Elimination: Dead globals are easier to optimize away.
    • Constant Detection: It allows the compiler to detect constant globals, which enables better propagation and dead code elimination during branching.
    • Resource Efficiency: On microcontrollers, constants can be stored in Flash memory instead of consuming precious RAM.
  2. Two ways to use WebAssembly with TinyGo

    dev

    TinyGo supports two primary patterns for WebAssembly development:

    1. Explicit Exports: Define and export specific functions to JavaScript using the //export <name> directive. You can also specify a custom Wasm module name (defaulting to env) using //go:wasm-module <module>.
    2. Main Function: Define a func main() which executes similarly to the standard Go implementation. This is typically used for standalone logic that runs upon instantiation.
  3. How TinyGo's partial evaluation of initialization code works

    dev

    TinyGo uses a partial evaluator for the runtime.initAll function to execute as much initialization code as possible at compile time. This process converts runtime initialization logic into static global data, which reduces code size and memory consumption.

    The partial evaluator consists of three components:

    1. Compiler: Extracts data from instructions so the interpreter can run without CGo calls (except for certain instructions like runtime.alloc).
    2. Interpreter: Executes instructions at compile time. If it encounters an instruction it cannot interpret (e.g., memory-mapped I/O), it triggers a rollback.
    3. Memory Manager: Manages memory using object types (backing storage) and value interfaces (local working values). It uses a per-function memory view to allow rolling back execution without leaving traces in the global state if an instruction must be deferred to runtime.
  4. Serve WebAssembly files with correct Content-Type

    dev

    Browsers require WebAssembly files to be served with the Content-Type: application/wasm HTTP header. If this header is missing, the module will fail to run.

    When building a local server in Go to serve your Wasm files, ensure you intercept .wasm requests to set this header.

    package main
    
    import (
    	"log"
    	"net/http"
    	"strings"
    )
    
    const dir = "./html"
    
    func main() {
    	fs := http.FileServer(http.Dir(dir))
    	log.Print("Serving " + dir + " on http://localhost:8080")
    	http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
    		resp.Header().Add("Cache-Control", "no-cache")
    		if strings.HasSuffix(req.URL.Path, ".wasm") {
    			resp.Header().Set("content-type", "application/wasm")
    		}
    		fs.ServeHTTP(resp, req)
    	}))
    }
  5. Compile programs for WebAssembly (WASM/WASI)

    dev

    TinyGo supports compiling for both browser-based WebAssembly (WASM) and server-side/edge WebAssembly System Interface (WASI) runtimes like Fastly Compute, Fermyon Spin, and wazero.

    To compile a program for a WASI Preview 1 runtime, use the tinygo build command with -buildmode=c-shared, -o for the output filename, and -target=wasip1.

    Alternatively, you can use Go 1.24+ style environment variables (GOOS and GOARCH) to specify the target.

    tinygo build -buildmode=c-shared -o add.wasm -target=wasip1 add.go
  6. Use custom build phase hooks for Docker Hub

    dev

    This directory contains custom commands designed to be executed during specific Docker Hub build phases. To implement these, you must configure your Docker Hub build settings to trigger these scripts at the appropriate lifecycle stages. For detailed instructions on how to configure and use custom build phase hooks, refer to the official Docker documentation.

    https://docs.docker.com/docker-hub/builds/advanced/#custom-build-phase-hooks
  7. Compile and flash programs for embedded microcontrollers

    dev

    TinyGo can compile Go programs for over 150 different microcontroller boards. To compile and flash a program directly to a supported board, use the tinygo flash command with the -target flag specifying your board name.

    Example of flashing an Arduino Uno:

    tinygo flash -target arduino-uno examples/blinky1
  8. Build a statically linked TinyGo release tarball

    dev

    To create a portable, statically linked version of TinyGo that can be moved between systems without dependency issues, follow these steps to build LLVM, Clang, and LLD from source and bundle them into a tarball.

    Prerequisites

    Ensure you have the following installed:

    • Go (1.19+)
    • GNU Make
    • Standard build tools (gcc/clang)
    • git
    • CMake
    • Ninja

    Build Steps

    1. Download Sources: Clone the repository (using --recursive is recommended) and download LLVM sources:
      make llvm-source
    2. Build LLVM/Clang/LLD: Use the provided Makefile to build the LLVM components. Setting CC and CXX to clang can speed up this process:
      export CC=clang
      export CXX=clang++
      make llvm-build
    3. Build TinyGo: Compile the compiler itself:
      make
    4. Create Release: Generate the static tarball:
      make release

    Using the Release

    Extract the tarball and run the binary from the bin directory:

    tar -xvf build/release.tar.gz
    ./tinygo/bin/tinygo help
    # Full sequence for a static release build
    make llvm-source
    export CC=clang
    export CXX=clang++
    make llvm-build
    make
    make release