Skip to content

ajokela/llvm-sampo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

LLVM Backend for Sampo 16-bit RISC CPU

This repository contains an LLVM backend for the Sampo 16-bit RISC CPU, enabling compilation of Rust, C, and other LLVM-based languages for custom Sampo hardware.

Overview

Sampo is a custom 16-bit RISC processor designed with clean RISC principles and Z80-inspired features. This LLVM backend provides:

  • GlobalISel-based instruction selection - Modern, modular code generation
  • Complete type legalization - Handles 8/16/32/64-bit operations on 16-bit hardware
  • Rust support - Full libcore and liballoc compilation
  • Calling convention - R4-R7 for arguments, R1 for return address

Architecture

Rust/C Source
      ↓
   Frontend (rustc/clang)
      ↓
   LLVM IR
      ↓
   GlobalISel (this backend)
      ↓
   Sampo Assembly
      ↓
   sasm Assembler
      ↓
   Binary → semu Emulator / FPGA

Quick Start

Prerequisites

Install the required build tools:

macOS:

brew install cmake ninja python3 git

Ubuntu/Debian:

sudo apt install cmake ninja-build python3 git build-essential

Fedora:

sudo dnf install cmake ninja-build python3 git gcc gcc-c++

Automated Bootstrap

The easiest way to build everything is with the bootstrap script:

git clone https://github.com/ajokela/llvm-sampo.git
cd llvm-sampo
./bootstrap.sh

This will:

  1. Clone and build LLVM with Sampo target support
  2. Clone and build Rust with Sampo cross-compilation
  3. Create a sysroot for compiling Rust programs

The toolchain is installed to ~/sampo-toolchain by default. Customize with:

WORK_DIR=/opt/sampo JOBS=8 ./bootstrap.sh

Step-by-Step Build (Alternative)

If you prefer manual control, follow these steps:

1. Build LLVM with Sampo Support

# Clone LLVM
git clone --depth 1 https://github.com/llvm/llvm-project.git
cd llvm-project

# Copy Sampo backend
git clone https://github.com/ajokela/llvm-sampo.git /tmp/llvm-sampo
cp -r /tmp/llvm-sampo/llvm/lib/Target/Sampo llvm/lib/Target/

# Register Sampo as an experimental target
sed -i.bak 's/set(LLVM_ALL_TARGETS/set(LLVM_ALL_TARGETS\n  Sampo/' llvm/CMakeLists.txt

# Configure and build
mkdir build && cd build
cmake -G Ninja \
  -DLLVM_ENABLE_PROJECTS="clang" \
  -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="Sampo" \
  -DLLVM_TARGETS_TO_BUILD="X86" \
  -DCMAKE_BUILD_TYPE=Release \
  -DLLVM_ENABLE_ASSERTIONS=ON \
  ../llvm

ninja -j$(nproc)

2. Build Rust for Sampo

# Clone Rust
git clone --depth 1 https://github.com/rust-lang/rust.git rust-sampo
cd rust-sampo

# Copy target spec
cp /tmp/llvm-sampo/rust-target/sampo_unknown_none.rs \
   compiler/rustc_target/src/spec/targets/

# Register the target (add to supported_targets! macro in mod.rs)
# Add: ("sampo-unknown-none", sampo_unknown_none),

# Create config.toml (adjust paths as needed)
cat > config.toml << 'EOF'
[llvm]
download-ci-llvm = false

[build]
target = ["x86_64-unknown-linux-gnu", "sampo-unknown-none"]
docs = false

[target.x86_64-unknown-linux-gnu]
llvm-config = "/path/to/llvm-project/build/bin/llvm-config"
EOF

# Build stage 1 with Sampo target
./x.py build --stage 1 library --target sampo-unknown-none

3. Create Sysroot

# Create sysroot directory structure
mkdir -p ~/sampo-sysroot/lib/rustlib/sampo-unknown-none/lib

# Copy compiled libraries
cp rust-sampo/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/sampo-unknown-none/lib/*.rlib \
   ~/sampo-sysroot/lib/rustlib/sampo-unknown-none/lib/
cp rust-sampo/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/sampo-unknown-none/lib/*.rmeta \
   ~/sampo-sysroot/lib/rustlib/sampo-unknown-none/lib/

Compiling Programs

Hello World in Rust

Create hello.rs:

#![no_std]
#![no_main]

extern "C" {
    fn putc(c: u8);
}

#[no_mangle]
pub extern "C" fn _start() {
    unsafe {
        putc(b'H');
        putc(b'i');
        putc(b'!');
        putc(b'\n');
    }
    loop {}
}

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

Compile with (using bootstrap):

~/sampo-toolchain/rustc-sampo --emit=asm hello.rs

Or manually:

rustc --target sampo-unknown-none \
      --sysroot ~/sampo-sysroot \
      -C opt-level=1 \
      --emit=asm \
      hello.rs

Output assembly:

_start:
    LIX  R4, 72       ; 'H'
    JALX putc
    LIX  R4, 105      ; 'i'
    JALX putc
    LIX  R4, 33       ; '!'
    JALX putc
    LIX  R4, 10       ; '\n'
    JALX putc
.loop:
    J    .loop

Assemble and Run

Use the Sampo assembler and emulator:

# Clone Sampo tools
git clone https://github.com/ajokela/sampo.git
cd sampo

# Build assembler and emulator
cargo build --release

# Assemble
./target/release/sasm hello.s -o hello.bin

# Run on emulator
./target/release/semu hello.bin

File Structure

llvm-sampo/
├── bootstrap.sh                # Automated build script
├── README.md
├── llvm/lib/Target/Sampo/      # LLVM backend
│   ├── Sampo.td                # Top-level TableGen
│   ├── SampoInstrInfo.td       # Instruction definitions
│   ├── SampoRegisterInfo.td    # Register definitions
│   ├── SampoCallingConv.td     # Calling convention
│   ├── SampoCallLowering.cpp   # GlobalISel call lowering
│   ├── SampoLegalizerInfo.cpp  # Type legalization rules
│   ├── SampoInstructionSelector.cpp # Instruction selection
│   ├── SampoAsmPrinter.cpp     # Assembly generation
│   ├── MCTargetDesc/           # MC layer (encoding, printing)
│   └── TargetInfo/             # Target registration
└── rust-target/
    └── sampo_unknown_none.rs   # Rust target specification

Sampo Calling Convention

Register Alias Purpose
R0 ZERO Always 0
R1 RA Return address
R2 SP Stack pointer
R3 GP Global pointer
R4-R7 A0-A3 Arguments / Return values
R8-R11 T0-T3 Temporaries (caller-saved)
R12-R15 S0-S3 Saved (callee-saved)

Troubleshooting

LLVM Build Fails with "Sampo not found"

Ensure Sampo is registered as an experimental target:

grep -r "Sampo" llvm-project/llvm/CMakeLists.txt

Rust Build Fails with "target not found"

Make sure the target is registered in compiler/rustc_target/src/spec/mod.rs:

supported_targets! {
    ("sampo-unknown-none", sampo_unknown_none),
    // ... other targets
}

Missing .rmeta Files

Both .rlib and .rmeta files must be in the sysroot:

ls ~/sampo-sysroot/lib/rustlib/sampo-unknown-none/lib/
# Should show: libcore-*.rlib, libcore-*.rmeta, liballoc-*.rlib, etc.

Out of Memory During Build

Reduce parallel jobs:

JOBS=2 ./bootstrap.sh

Or limit parallel link jobs in cmake:

cmake ... -DLLVM_PARALLEL_LINK_JOBS=1

Related Projects

License

This project is licensed under the Apache License 2.0 with LLVM Exceptions, consistent with the LLVM project.

Acknowledgments

This backend was developed with assistance from Claude Code (Anthropic). The iterative development process demonstrates how AI-assisted development can tackle complex compiler engineering challenges.

About

LLVM backend for the Sampo 16-bit RISC CPU - enables Rust compilation for custom hardware

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages