Skip to content

Latest commit

 

History

History
79 lines (73 loc) · 7.76 KB

CONTEXT.md

File metadata and controls

79 lines (73 loc) · 7.76 KB

CONTEXT.md - TinyCompiler

Read this before any task. Full plan: docs/PLAN.md.

What this is

A small multi-level compiler for a tiny tensor language (.tiny). One program, e.g. C = relu(A @ B), lowers to tiny-gpu SIMT assembly (.tgpu + launch.json) or to a tiny-tpu 4×4 tile schedule (.ttpu.json), then runs on the unmodified tiny-gpu simulator or the real tiny-tpu RTL (WASM). Fourth project after tiny-gpu, tiny-tpu and tiny-cpu. Nothing on screen is faked; host-side work is always labelled as host work.

Status

  • Phase 0 (scaffold + drive both targets from pytest): DONE. 17 tests green (runners + target contract), ruff/black clean.
  • Phase 1 (frontend + Graph IR + oracle): DONE. 90 tests green: lexer/parser/diagnostics (20 golden error messages), Graph IR printer/parser round trip, verifier, numpy oracle + independent scalar interpreter, 6 examples.
  • Phase 2 (pass manager + graph passes): DONE. Passes: shape-infer, FIXPOINT{canonicalize, const-fold, cse, dce}, fuse (matmul{epilogue=bias,relu}), range-check, legalize-tpu/gpu; --verify-each, --print-after-all, --time-passes.
  • Phase 3 (TPU backend): DONE. 287 tests green. Graph IR -> Tile IR (tinyc/tpu/) -> .ttpu.json (schema docs/ttpu.schema.json, spec docs/TTPU_FORMAT.md); runtime/ttpu-exec.mjs runs whole programs on the real RTL; cost model == measurement (pinned in tests/perf_baseline.json). tinyc FILE --target tpu --run works.
  • Phase 4 (GPU backend, -O0): DONE. 443 tests green. Graph IR -> memory plan -> Kernel IR -> linear-scan registers -> .tgpu + launch JSON (tinyc/gpu/, schema docs/launch.schema.json, spec docs/TGPU_FORMAT.md). Every example, including the chained-matmul mlp_gpu, runs bit-exact on the real simulator; the same program gives identical results on both chips. tinyc FILE --target gpu --run.
  • Phase 5 (GPU kernel optimizations): DONE. 557 tests green. -O0/-O1/-O2: licm, strength-reduce, kernel-dce | + shared-tile, unroll, fold. Measured table in docs/ARCHITECTURE.md (generated by scripts/opt_table.py, drift-tested). -O2 has strictly fewer global loads than -O0 on every matmul example (exact formula tested). Cost model is now an abstract interpretation.
  • Phase 6 (differential fuzzing + hardening): DONE. 611 tests green. tests/test_fuzz.py runs random programs through gpu -O0/1/2 and tpu -O0/1 on the real chips against the source oracle; two independent 5000-program runs were green; 13-program corner-case corpus in tests/regressions/; fuzz-nightly.yml; docs/VERIFICATION.md. The CLI compiler is now publishable on its own.
  • Phase 7 (Compiler Explorer): DONE for the MVP. 620 tests green. web/ (Astro + React, static). Pyodide worker runs runtime/web_api.py (compile both chips, run tiny-gpu simulator, oracle); tiny-tpu RTL (WASM) runs in the page via runtime/ttpu-exec.mjs. Verified in a real browser: default example bit-exact on both chips; mlp_gpu at -O2 bit-exact on gpu and rejected on tpu with the legality message; phone width (375px) has no horizontal scroll. Not done: CodeMirror editor (plain textarea), the TPU PE-grid animation and pass-by-pass replay, Vitest/Playwright tests, the L1-L4 level tabs beyond the four views.
  • Phase 7 follow-up (done): web toolchain is pnpm; pnpm lint|typecheck|test|e2e in web/; Playwright smoke in CI (web.yml).
  • Phase 9 (done): L2 TPU array view: the RTL's per-cycle state played on tiny-tpu's PEGrid (TpuSchedule.tsx).
  • Phase 10 (done): CodeMirror source editor (SourceEditor.tsx).
  • Phase 11 (done): landing-page hero clip (web/public/hero.webm, recorded by web/scripts/record-hero.mjs).
  • Done: main is the GitHub default branch and holds the full project; Vercel production serves it.
  • Open, needs the user: README links in the tiny-gpu and tiny-tpu repos. Branches docs/link-tiny-compiler are pushed to tinycomputers/tiny-gpu and tinycomputers/tiny-tpu (those repos moved there); no PRs opened yet.

Fixed decisions (do not re-litigate; change only via docs/ARCHITECTURE.md)

Decision Choice
Language .tiny, static shapes, rank ≤ 2, types i8/i32, ops @, + (row broadcast), relu
Implementation Python 3.12 + numpy; runs in the browser via Pyodide (no TS re-implementation)
IR levels Graph IR (SSA, typed, loc-tagged) → Tile IR (TPU) / Kernel IR (GPU); printer + verifier + interpreter for each
Targets Submodules at pinned commits; v1 does not modify either chip
GPU ABI R5 = block_id, R6 = thread_in_block, R7 = tid are reserved; addresses baked in as immediates; no kernel args
GPU mapping One thread per output element: block = row i, thread = column j
GPU regalloc Linear scan, budget 16 by default, no spilling (compile error)
TPU mapping 4×4 int8 passes, zero padding, K-accumulation + relu/bias as host.* ops
TPU executor Exactly one: runtime/ttpu-exec.mjs (Node in tests, browser in UI). tinyc/tpu/interp.py is a second, independent reference reader of the same JSON spec, never a second executor
TPU host work acc/epilogue/host_add/host_relu/alloc are host commands, counted separately from chip passes everywhere
GPU mapping One thread per output element: block = row (R5), thread = column (R6), tid (R7) = flat index. One kernel per Graph op (fused = one). Kernel code is read-only on R5-R7 (asserted + checked at run time)
GPU cycles A thread needs taken-branches+1 cycles, a launch the max. Report instructions with cycles. estimate gives min/max (relu is data dependent), loads/stores exact; gpu.interp counts must equal the simulator's
-O levels -O0 naive; -O1 graph passes + kernel licm/strength-reduce; -O2 + shared-tile, unroll, fold (gpu only; tpu -O2 == -O1). Kernel passes are switchable by name (--disable)
Kernel soundness Lowering assigns every temporary once; only accumulators/pointers/counters are reassigned. Passes may move or rewrite single-assignment values freely
GPU barrier SYNCB only at kernel top level; waiting threads re-execute it each step (counts modelled exactly in gpu.interp); out-of-range shared access is a fault in the interpreter
GPU asm rules Labels are L<n> (a label starting with R is parsed as a register); a kernel never ends with an if (stray extra cycle); one fresh simulator per launch at its final size, memory carried over
TPU cost passes * 15 RTL cycles; utilization = useful MACs (M*K*N) / (64 * passes)
Overflow range-check proves every value fits i32 by intervals; otherwise reject (tiny-gpu raises on overflow). i32 inputs are assumed within ±2^20 (PassContext.i32_input_range); runners must respect it
Fusion matmul{epilogue=bias,relu}; the fused op takes the outermost absorbed op's loc. Backends must lower the epilogue
Verification Oracle (numpy) + per-pass translation validation + real targets + Hypothesis differential + golden + perf pins

Target gotchas (see PLAN §2)

  • tiny-gpu runs straight-line instructions in one "cycle": report instructions issued as well.
  • tiny-gpu set_grid resize leaves R7 = 0 for threads it adds: build the simulator at its final size.
  • tiny-tpu passes always reload weights and start from psum = 0; getCycleCount() reports 15 per pass (14 + final latch).
  • tiny-tpu silently truncates operands to int8: the compiler must reject non-i8 itself.
  • Never run black ./formatters over targets/ (submodules); pyproject excludes it.
  • Python deps: pip install -e targets/tiny-gpu --no-deps (tiny-gpu's matplotlib/imageio are not needed).

Conventions

  • Python: 4-space indent, Black + Ruff, tests under tests/, fixed seed 42.
  • Web: Astro + React islands + Tailwind, TypeScript strict; WASM/Pyodide only in client:only="react" islands and workers.
  • docs/TTPU_FORMAT.md and its JSON Schema must stay in sync with tinyc/tpu/emit.py.