Skip to content

Architecture

jihoo aims to be an easy language that can still go all the way down to bare syscalls. One surface language is lowered to one IR, JIR; that IR is either run by a VM or compiled by LLVM.

┌─────────────── Rust ───────────────┐ ┌──── C++ ─────┐
source.jh ──► │ jihoo-syntax ─► jihoo-sema ──► JIR │ ──► │ jihoo-llc │ ──► .o ─┬─► cc + libc ──► program (native)
│ (parse) (typecheck │ │ .jir │ (LLVM 21) │ └─► ld.lld ─────► static ELF (freestanding)
│ + lower) ▼ │ └──────────────┘
│ jihoo-vm│
└────────────────────────────────────┘
path language what
crates/jihoo-syntax Rust lexer, parser, AST, and the module loader
crates/jihoo-sema Rust type checking and lowering from the AST to JIR, compile-time evaluation, macros
crates/jihoo-ir Rust JIR data types, the text printer and reader, layouts, the verifier
crates/jihoo-vm Rust the register VM, its task scheduler and the GC heap
crates/jihoo-cli Rust the jihoo command
backend-llvm C++ jihoo-llc: JIR text → LLVM → object file
lib jihoo the standard library
tests/diff jihoo programs that must behave the same on the VM and natively
site Astro this website
  1. Load (crates/jihoo-syntax/src/loader.rs). The root file and every file it imports, transitively, are parsed into one AST per module. Each file is loaded once; positions carry a file number, so errors name the right file. The target (x86_64 or aarch64 Linux) matters only here: import a prefers a.<target>.jh to a.jh, so the rest of the compiler only ever sees the target’s files. The target is then recorded in the JIR for the backend.
  2. Analyze (crates/jihoo-sema). Item macros are expanded, then every function is type checked and lowered to typed JIR in one pass, generic ones once per instance. Constants, comptime expressions and macros run on the VM along the way. See Compiler.
  3. Verify (crates/jihoo-ir/src/verify.rs). The JIR module is checked on its own: register types, operand types, terminators, and that the profile allows every instruction. This catches frontend bugs before a backend sees them.
  4. Run or compile. A native module (the default) or a freestanding one is printed as text, compiled by jihoo-llc (LLVM backend) and linked by jihoo build (which jihoo run uses, then runs the result). A hosted module runs on the VM (VM and GC).

The frontend (Rust) and the LLVM backend (C++) only talk through .jir files (JIR). This keeps the IR an explicit, versioned contract (Versions), lets the backend be developed and tested on its own with hand-written .jir (Testing), and keeps the Rust build free of LLVM. jihoo reads .jir files back too (crates/jihoo-ir/src/parse.rs), so a module written once can be run on the VM or built natively without its source.

The typing rules of operators live once, in crates/jihoo-ir/src/types.rs, and are used both by the type checker and by the IR verifier, so the two cannot disagree. The C layout rules (crates/jihoo-ir/src/layout.rs) are recorded in JIR and checked again by the backend against the real target.