remora/
├── .env.example # Template for local build paths (copy to .env)
├── jax/ # JAX model definitions and verification scripts
├── mlir/
│ ├── stablehlo/ # StableHLO IR exported from JAX
│ └── linalg/ # Linalg IR lowered from StableHLO
├── compiler/
│ ├── main.cpp # Pass pipeline, JIT harness, kernel invocation
│ ├── CMakeLists.txt # Build config (reads MLIR_DIR, STABLEHLO_ROOT/BUILD from .env)
│ └── build/ # CMake build output (gitignored)
├── md/ # Design docs and quiz
└── scripts/
├── bootstrap.sh # Build MLIR + stablehlo-opt from source
├── build.sh # Build remora-compiler (requires .env)
├── run_elementwise.sh # JIT-run elementwise kernel, print output
├── run_projection.sh # JIT-run projection kernel, print output
├── attention_elementwise_lower_to_linalg.sh # Lower elementwise StableHLO → Linalg
├── attention_projection_lowered_to_linalg.sh # Lower projection StableHLO → Linalg
└── elementwise-explore.sh # Step through lowering passes interactively
gitcmake>= 3.20ninjapython3- ~30 GB disk space (LLVM build is large)
Run the bootstrap script from anywhere — it resolves all paths relative to the repo root and clones dependencies into build-deps/ by default.
scripts/bootstrap.shTo use a custom build directory:
scripts/bootstrap.sh --build-dir /path/to/your/build-deps
# or
BUILD_DIR=/path/to/your/build-deps scripts/bootstrap.shWhen the build finishes, the script prints the paths you'll need for the next step.
The build scripts read local paths from a .env file (gitignored). Copy the example and fill in the paths printed by bootstrap.sh:
cp .env.example .envEdit .env:
MLIR_DIR=/path/to/build-deps/llvm-build/lib/cmake/mlir
STABLEHLO_ROOT=/path/to/build-deps/stablehlo
STABLEHLO_BUILD=/path/to/build-deps/stablehlo/buildIf you used the default build-deps/ location these will be <repo-root>/build-deps/....
sh scripts/build.shThis sources .env, runs CMake, and produces compiler/build/remora-compiler.
remora-compiler lowers a StableHLO file through the full pass pipeline to LLVM dialect, JIT-compiles it via LLVM ORC, and executes it on CPU. Use --kernel to select the test harness matching the input file.
compiler/build/remora-compiler mlir/stablehlo/simple_attention_elementwise.mlir --kernel=elementwise
compiler/build/remora-compiler mlir/stablehlo/simple_attention_projection.mlir --kernel=projectionOr via the wrapper scripts:
sh scripts/run_elementwise.sh # relu(x + bias), expects 0.5
sh scripts/run_projection.sh # matmul(x, w), expects ~1.0Pass --mlir-print-ir-after-all to dump IR after each lowering pass:
compiler/build/remora-compiler mlir/stablehlo/simple_attention_elementwise.mlir --kernel=elementwise --mlir-print-ir-after-allThe jax/verify_*.py scripts run the same kernels in JAX with identical inputs and print output in the same format. Both should produce bit-identical results.
diff <(sh scripts/run_elementwise.sh) <(jax/.venv/bin/python jax/verify_elementwise.py)
diff <(sh scripts/run_projection.sh) <(jax/.venv/bin/python jax/verify_projection.py)These scripts use stablehlo-opt to lower to Linalg and write the result to mlir/linalg/:
scripts/attention_elementwise_lower_to_linalg.sh
scripts/attention_projection_lowered_to_linalg.shTo see what the fusion pass does — StableHLO broadcast/add/relu collapsed into a single fused linalg.generic — diff the input against the output:
diff mlir/stablehlo/simple_attention_elementwise.mlir \
mlir/linalg/attention_elementwise_lowered_to_linalg.mlirSet STABLEHLO_OPT if stablehlo-opt is not on your PATH:
export STABLEHLO_OPT=/path/to/build-deps/stablehlo/build/bin/stablehlo-optscripts/elementwise-explore.shRuns the elementwise file through several progressive lowering steps and prints each result to stdout.
cd jax
pip install -r requirements.txt
python simple_attention_elementwise.py > ../mlir/stablehlo/simple_attention_elementwise.mlir
python simple_attention_projection.py > ../mlir/stablehlo/simple_attention_projection.mlirBoth scripts print the exported StableHLO module to stdout.