Skip to content

Commit b7e9d8f

Browse files
committed
mainarch: a Rust inference stack that talks straight to the AMD kernel driver
Running a large language model on an AMD GPU normally means going through ROCm: the HIP runtime, the HSA runtime beneath it, the thunk library beneath that, and only then the kernel driver that actually talks to the card. mainarch skips them. It opens /dev/kfd, builds the dispatch packet by hand, rings the doorbell, and waits on the signal the command processor decrements. From there it goes up. GPU virtual memory, AQL queues, a code-object loader written from scratch, all-reduce over XGMI, FlashDecoding attention with paged FP4 KV, a decoder layer, and an autoregressive token loop, in one typed Rust workspace with nothing borrowed from a vendor runtime. At the top of it a real model runs. OLMo 2 from the Allen Institute for AI serves over /v1/chat/completions on an MI355X, at full depth and full vocabulary from a real checkpoint. OLMo 2 rather than a more familiar model because it is open source rather than open weights: AI2 publishes the corpus, the training code and every intermediate checkpoint, so nothing between the training data and a token leaving a hand-built AQL packet is a black box. crates/mainarch-sys hand-encoded amdkfd ioctls crates/mainarch-core device, memory, queues, code objects, attention, decoder layer, model loop, weights, OLMo 2 crates/mainarch-collectives collectives and the rccl-tests-shaped harness crates/mainarch-cli the mainarch binary kernels/ 187 GPU kernels and the gfx950 cross-compile demo/sandbox/ a GPU-free demo server behind an OpenAI-shaped seam examples/model-api-plugin/ a standalone external model package `just demo` builds the workspace, proves what the machine can do, and serves the demo page, taking the live raw-KFD/AQL lane when an AMD GPU is reachable and a CPU-only lane when one is not. `just olmo` runs the real model. Correctness is gated rather than asserted, and the gates are written to fail when a claim is false rather than when arithmetic is merely imprecise. The multi-head attention kernel is checked bitwise against the grouped-query kernel it derives from, and then against a perturbation that a kernel ignoring head indices would survive. The QK-norm kernel is checked against an f64 reference, and then against a perturbation only a projection-wide RMS can propagate. The post-norm kernel is checked against both orderings so pre-norm cannot pass. This is a reference architecture and a working prototype, not a production serving stack. It serves one request at a time, there is no batching, no scheduling across sequences, no auth and no quota, and nothing on the OLMo path is fused or tuned. The README's scope section says where every edge is. Apache License 2.0.
0 parents  commit b7e9d8f

163 files changed

Lines changed: 404942 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/Dockerfile

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# mainarch dev image. A clean Rust toolchain that talks to AMD silicon at the
2+
# KERNEL ABI. Deliberately NO ROCm / HIP / HSA runtime: this project replaces
3+
# that stack rather than building on it. The only AMD-specific things needed are
4+
# * the kernel UAPI headers (linux/kfd_ioctl.h) shipped by linux-libc-dev
5+
# * libdrm's amdgpu_drm.h for the DRM render-node ioctls
6+
# * LLVM/clang, whose upstream amdgpu backend can emit gfx950 ISA on its own
7+
# The GPU, when present, is reached purely via /dev/kfd + /dev/dri passthrough
8+
# (see .devcontainer/gpu/devcontainer.json). The image itself builds and runs
9+
# fine on a machine with no AMD GPU at all, and every CPU-only path still works.
10+
ARG BASE=docker.io/library/ubuntu:24.04
11+
FROM ${BASE}
12+
13+
ENV DEBIAN_FRONTEND=noninteractive \
14+
LANG=C.UTF-8
15+
16+
# --- system: build toolchain, kernel/driver ABI headers, debug + dev ergonomics
17+
RUN apt-get update && apt-get install -y --no-install-recommends \
18+
build-essential clang lld llvm libclang-dev \
19+
cmake ninja-build pkg-config \
20+
linux-libc-dev libdrm-dev libelf-dev libnuma-dev zlib1g-dev \
21+
mold \
22+
git curl ca-certificates gnupg openssh-client \
23+
gdb strace ltrace \
24+
vim tmux htop jq ripgrep bsdmainutils file less \
25+
python3 \
26+
&& rm -rf /var/lib/apt/lists/*
27+
28+
# --- Rust toolchain (system-wide so root and any user share it)
29+
ENV RUSTUP_HOME=/usr/local/rustup \
30+
CARGO_HOME=/usr/local/cargo \
31+
PATH=/usr/local/cargo/bin:$PATH
32+
# NB: rustup-init takes one component per -c flag. clippy + rustfmt already come
33+
# from `--profile default`; only rust-analyzer + rust-src need adding.
34+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
35+
| sh -s -- -y --default-toolchain stable --profile default \
36+
-c rust-analyzer -c rust-src \
37+
&& rustc --version && cargo --version
38+
39+
# --- iteration harness: `just` task runner, nextest, watch mode
40+
# Installed from crates.io rather than a prebuilt release, so this works on any
41+
# builder without needing BuildKit cache mounts or a static-musl downloader.
42+
RUN cargo install --locked just cargo-nextest cargo-watch \
43+
&& just --version && cargo-nextest --version && cargo-watch --version
44+
45+
# mold keeps incremental links snappy on a workspace this size
46+
ENV RUSTFLAGS="-C link-arg=-fuse-ld=mold"
47+
48+
WORKDIR /workspaces
49+
LABEL org.opencontainers.image.title="mainarch-dev" \
50+
org.opencontainers.image.description="Rust + raw AMD kernel ABI dev harness (no ROCm)"

.devcontainer/devcontainer.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "mainarch (CPU)",
3+
"build": {
4+
"dockerfile": "Dockerfile"
5+
},
6+
"mounts": [
7+
"source=mainarch-cargo-registry,target=/usr/local/cargo/registry,type=volume"
8+
],
9+
"customizations": {
10+
"vscode": {
11+
"settings": {
12+
"editor.formatOnSave": true,
13+
"rust-analyzer.check.command": "clippy",
14+
"[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" },
15+
"files.watcherExclude": { "**/target/**": true }
16+
},
17+
"extensions": [
18+
"rust-lang.rust-analyzer",
19+
"vadimcn.vscode-lldb",
20+
"tamasfe.even-better-toml",
21+
"skellock.just"
22+
]
23+
}
24+
},
25+
"remoteUser": "root",
26+
"postCreateCommand": "bash .devcontainer/post-create.sh"
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "mainarch (AMD GPU passthrough)",
3+
"build": {
4+
"dockerfile": "../Dockerfile",
5+
"context": ".."
6+
},
7+
"runArgs": [
8+
"--device=/dev/kfd",
9+
"--device=/dev/dri",
10+
"--security-opt=seccomp=unconfined",
11+
"--group-add=keep-groups",
12+
"--ipc=host",
13+
"--cap-add=SYS_PTRACE"
14+
],
15+
"mounts": [
16+
"source=mainarch-cargo-registry,target=/usr/local/cargo/registry,type=volume"
17+
],
18+
"customizations": {
19+
"vscode": {
20+
"settings": {
21+
"editor.formatOnSave": true,
22+
"rust-analyzer.check.command": "clippy",
23+
"[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" },
24+
"files.watcherExclude": { "**/target/**": true }
25+
},
26+
"extensions": [
27+
"rust-lang.rust-analyzer",
28+
"vadimcn.vscode-lldb",
29+
"tamasfe.even-better-toml",
30+
"skellock.just"
31+
]
32+
}
33+
},
34+
"remoteUser": "root",
35+
"postCreateCommand": "bash .devcontainer/post-create.sh"
36+
}

.devcontainer/post-create.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
# Runs once when the devcontainer is created. Nothing here is required for the
3+
# build; it just tells you which lane you are in.
4+
set -u
5+
6+
echo
7+
rustc --version
8+
just --version 2>/dev/null || true
9+
echo
10+
11+
if [ -e /dev/kfd ] && [ -d /sys/class/kfd/kfd/topology/nodes ]; then
12+
echo "AMD kernel driver visible: /dev/kfd is present."
13+
ls -l /dev/kfd /dev/dri/renderD* 2>/dev/null | head -4
14+
echo "GPU lane available. 'just demo' will run the live KFD/AQL path."
15+
else
16+
echo "No /dev/kfd in this container: running the CPU-only lane."
17+
echo "Everything in 'just tour' still works; the live GPU proofs are skipped."
18+
echo "On an AMD GPU host, reopen using the '.devcontainer/gpu' configuration."
19+
fi
20+
21+
echo
22+
echo "mainarch dev harness ready (no ROCm anywhere in this image)."
23+
echo "Next: just demo"
24+
echo

.gitattributes

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
* text=auto
2+
3+
*.rs text eol=lf
4+
*.toml text eol=lf
5+
*.md text eol=lf
6+
*.yml text eol=lf
7+
*.yaml text eol=lf
8+
*.json text eol=lf
9+
*.sh text eol=lf
10+
*.py text eol=lf
11+
*.html text eol=lf
12+
*.css text eol=lf
13+
*.js text eol=lf
14+
*.cl text eol=lf
15+
*.txt text eol=lf
16+
justfile text eol=lf
17+
Dockerfile* text eol=lf
18+
19+
# Byte-exact fixtures: the model API gate compares these verbatim, so they must
20+
# not pick up CRLF on a Windows checkout.
21+
*.receipt text eol=lf
22+
23+
# The compiled gfx950 HSA code object is loaded by our own ELF reader and its
24+
# sha256 is asserted; never let it through a text filter.
25+
*.co binary
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Bug Report
2+
3+
## Summary
4+
5+
Describe the observed bug and the expected behavior.
6+
7+
## Validation Tier
8+
9+
- [ ] CPU-only
10+
- [ ] Single GPU
11+
- [ ] Multi-GPU / XGMI
12+
- [ ] Baseline comparison
13+
- [ ] Documentation
14+
15+
## Environment
16+
17+
- commit:
18+
- host kernel:
19+
- GPU model / gfx target:
20+
- container or host runtime:
21+
- relevant device access (`/dev/kfd`, `/dev/dri`):
22+
23+
## Reproduction
24+
25+
Command:
26+
27+
```bash
28+
29+
```
30+
31+
Environment variables:
32+
33+
```text
34+
35+
```
36+
37+
Observed output:
38+
39+
```text
40+
41+
```
42+
43+
Expected output:
44+
45+
```text
46+
47+
```
48+
49+
## Scope
50+
51+
- Does this involve GPU execution?
52+
- Does this involve a performance claim?
53+
- Does this involve model API metadata/preflight only?
54+
- Does this involve secrets, private hardware logs, hostnames, or cluster
55+
identifiers that should be redacted?
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Model API Gap
2+
3+
## Model Or Architecture
4+
5+
Name the model family, architecture feature, or primitive pattern that is not
6+
represented cleanly by the current `mainarch-core::model_api` surface.
7+
8+
## Exact Gap
9+
10+
- missing primitive
11+
- missing tensor/cache shape contract
12+
- missing lowering route metadata
13+
- missing checkpoint binding metadata
14+
- missing runtime preflight/reporting surface
15+
- unclear public API ergonomics
16+
17+
## Contract Tier
18+
19+
Select the narrowest tier this gap affects:
20+
21+
- [ ] public model authoring contract
22+
- [ ] static runtime metadata contract
23+
- [ ] experimental execution boundary
24+
25+
## Minimal Graph Shape
26+
27+
List the smallest tensor shapes, cache layout, stage boundary, or op sequence
28+
that demonstrates the gap.
29+
30+
```text
31+
32+
```
33+
34+
## Current Workaround
35+
36+
Describe whether the gap is currently represented as an explicit `Gap`, a custom
37+
runner path, a fused native route, or not represented at all.
38+
39+
## Evidence
40+
41+
Include the command, test, example, or docs section that demonstrates the gap.
42+
If the gap crosses into live graph execution, include the hardware correctness
43+
oracle and name the submission blockers that would need to be removed. Otherwise
44+
state that the gap is CPU-only metadata, preflight, or documentation scope.
45+
Do not claim GPU execution or performance unless the matching hardware evidence
46+
is included.
47+
48+
## Negative Scope
49+
50+
State what the issue does not prove or request: for example, no GPU allocation,
51+
no queue submission, no kernel execution, no throughput claim, or no API change.

.github/pull_request_template.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Pull Request
2+
3+
## Exact claim
4+
5+
State the narrow claim this PR makes. Keep it at the exact scale and behavior
6+
the evidence validates.
7+
8+
## Change type
9+
10+
- [ ] Runtime / kernel ABI / GPU execution
11+
- [ ] Kernels
12+
- [ ] Model API metadata or preflight
13+
- [ ] Checkpoint / weight loading
14+
- [ ] Collectives / performance
15+
- [ ] Documentation
16+
- [ ] Tooling or tests
17+
18+
## Validation
19+
20+
- [ ] `just ci` passes
21+
- [ ] Focused model API checks, if `model_api` changed
22+
(`python3 tools/check_model_api_public_examples.py`)
23+
- [ ] Hardware gate, if the claim involves GPU execution
24+
- [ ] Apples-to-apples baseline, if the claim involves performance.
25+
Say which configuration the baseline ran in
26+
27+
## Evidence
28+
29+
Paste the commands and their output. For hardware claims include the GPU model,
30+
gfx target, and driver version.
31+
32+
```
33+
34+
```
35+
36+
## Negative scope
37+
38+
What this change does *not* prove.
39+
40+
## Notes for reviewers
41+
42+
Assumptions, unresolved blockers, unsupported hardware, or intentionally
43+
deferred follow-up work.

0 commit comments

Comments
 (0)