A Go compiler and language implementation for embedded systems.
Note: This compiler is under active development and changes often.
- ARM Cortex-M0+
- ARM Cortex-M4
- ARM Cortex-M7
More to come.
- Go 1.21 or later
- Clang — recommended over GCC; cross-compiles to all supported targets out of the box. On Windows, only the
x86_64-pc-windows-gnuvariant of clang is compatible with Go. - CMake and Ninja — for building the LLVM/MLIR submodule and the GoIR dialect
- System libraries — on Debian/Ubuntu:
sudo apt install clang-20 lld-20 cmake ninja-build libzstd-dev
Heads-up on
clang-21: as of this writing,clang-21crashes when compiling parts of MLIR. Stick withclang-20(or GCC) as the host compiler for building LLVM. This is a clang frontend bug, not a SiGo issue.
The build has three phases. The first is one-time; the latter two iterate while you develop.
LLVM lives at a fixed location independent of SiGo's build mode and is always built Release. You only rebuild it when the submodule moves.
git clone https://github.com/waj334/sigo.git --recurse-submodules
cd sigo
make CC=clang-20 CXX=clang++-20 LD=lld install-llvmThis installs LLVM to build/llvm/install. To use a faster linker for this step (highly recommended — links 10–20x faster than GNU ld):
make CC=clang-20 CXX=clang++-20 LD=lld install-llvm # LLVM's lld
make CC=clang-20 CXX=clang++-20 LD=mold install-llvm # mold, often even fastermake sigoThis builds GoIR, the sysroots (picolibc + compiler-rt + lwIP for each target architecture), and finally the sigoc binary at ./bin/sigoc. A debug build is the default; for a release build:
make SIGO_BUILD_RELEASE=1 sigoGoIR and sigoc are rebuilt in the same mode (Debug or Release), but LLVM is shared between them. Switching modes does not trigger an LLVM rebuild.
./bin/sigoc --helpThree variables control which compiler and linker the build uses:
| Variable | Purpose | Default |
|---|---|---|
CC |
Host C compiler | system cc |
CXX |
Host C++ compiler | system c++ |
LD |
Linker name passed to -fuse-ld |
ld (system default) |
LD accepts short names (lld, mold, gold), versioned names (lld-20, ld.lld-20), or absolute paths (/usr/bin/ld.lld-20). All three forms are normalized internally.
# Versioned toolchain
make CC=clang-20 CXX=clang++-20 LD=lld-20 install-llvm sigo
# Absolute paths if needed
make CC=/opt/llvm-20/bin/clang \
CXX=/opt/llvm-20/bin/clang++ \
LD=/opt/llvm-20/bin/ld.lld \
install-llvm sigoIf these are omitted, CMake picks the host system default.
The sysroots are cross-compiled to bare-metal ARM/RISC-V targets, so they need a clang (GCC isn't a cross-compiler in the same way). By default, the sysroot build follows CC if it's clang-flavored, otherwise it falls back to which clang. You can override independently:
make SYSROOT_CC=clang-20 SYSROOT_CXX=clang++-20 SYSROOT_LD=lld-20 sysrootsIf you already have LLVM/MLIR built somewhere, point at it instead of building one:
make LLVM_PREFIX=/usr/local sigoThis skips install-llvm entirely. Run make env first to confirm what got resolved.
install-llvm Build and install LLVM (one-time)
sigo Build sigoc (default target)
release Same as `sigo` but for release builds
sysroots Build all sysroots (libc, compiler-rt, lwIP per target)
test Run SSA compiler tests
env Print resolved build flags (useful for debugging)
clean Remove all build artifacts including LLVM
clean-sigo Remove only the sigoc binary
clean-sysroots Remove all sysroots
clean-goir Remove the GoIR build tree
clean-llvm Remove the LLVM build tree (forces a full rebuild)
configure Configure both LLVM and GoIR CMake builds
reconfigure Force re-configure (useful after toolchain changes)
A firmware image is built with the build subcommand. A target CPU must be specified via --cpu:
./bin/sigoc build -j8 --cpu atsame51g19a -O0 -g \
-o /path/to/output/firmware.elf \
./examples/arm/samx51/blinkyFull flag reference:
Usage:
sigoc build [flags]
Flags:
--cpu string target cpu
--ctypenames use C type names for primitives in debug information
-g, --debug generate debug information
--dump-ir dump the IR
--float string floating-point mode (softfp, hardfp) (default "softfp")
-h, --help help for build
-j, --jobs int number of concurrent builds (default NCPU)
-O, --opt string optimization level (default "0")
-o, --output string output file (default ".")
-s, --stack-size int stack size of each goroutine (default 2048)
-t, --tags string build tags
-v, --verbose string verbosity level
--work do not delete the work directory upon build
make sigo fails immediately with "LLVM not found" — Run make install-llvm first. The error message tells you which variable would let you point at an existing LLVM install instead.
Linking takes forever — Add LD=lld (or LD=mold) to your make invocation. Default GNU ld can take minutes to link sigoc; lld brings it down to seconds.
clang-21 crashes during the LLVM build — Known issue. Use clang-20 or GCC for the host build (make CC=clang-20 CXX=clang++-20 install-llvm).
-fuse-ld=ld.lld-NN rejected as "invalid linker name" — Should be fixed in the current Makefile, but if you hit it: clang only accepts short suffixes for -fuse-ld. The Makefile auto-strips ld. prefixes, so LD=ld.lld-22 becomes -fuse-ld=lld-22 internally. If you're building outside of make, pass -fuse-ld=lld-22, not -fuse-ld=ld.lld-22.
Windows: symlink errors during sysroot build — Enable Developer Mode in Windows settings.
Need to see what flags make is actually passing? — make env prints the resolved CGO_CFLAGS, CGO_LDFLAGS, etc., plus runs go env with them set.
See LICENSE.