ruby/ruby
Architecture
Ruby is implemented as a tree of cooperating subsystems written mostly in C, with selected components in Rust (the JITs) and Ruby (the standard library and many built-in method bodies). At the highest level, source code flows from text → AST → bytecode (an iseq) → either the interpreter loop or one of the JITs, while the rest of the runtime — GC, threading, IO, and core classes — backs each step.
Top-level execution flow
graph LR
src[Ruby source code] -->|tokenize + parse| parser
parser[Parser: parse.y or Prism]
parser -->|AST| compiler[Bytecode compiler\ncompile.c / prism_compile.c]
compiler -->|iseq| vm[VM dispatch loop\nvm.c / vm_exec.c]
vm -->|hot code| yjit[YJIT\nyjit/]
vm -->|opt-in| zjit[ZJIT\nzjit/]
yjit -->|machine code| cpu[CPU]
zjit -->|machine code| cpu
vm -->|interp ops| cpu
vm <-->|alloc / sweep| gc[Garbage collector\ngc.c / gc/]
vm <-->|threads / IO| os[OS scheduler]Source text is lexed and parsed into an AST. The compiler walks the AST to emit a stack-based bytecode instruction sequence (an iseq, defined in iseq.h/iseq.c). The VM dispatches those instructions in a switch-or-threaded loop (vm_exec.c, generated from insns.def). When YJIT or ZJIT is enabled, it observes hot iseqs and generates native machine code that the VM jumps into in place of interpretation.
Parsers
Ruby ships with two parsers:
parse.y— the historical Bison grammar (now built with Lrama, a Ruby-implemented Bison-compatible parser generator undertool/lrama/). It produces theNODEtree (node.h,node.c).prism/— a hand-written portable C parser (originallyYARP) that produces a typed AST (prism/prism.h,prism/config.yml). Prism is also distributed as a standalone library and powers tools like Ruby LSP, RuboCop, and Sorbet.
The active parser is selected at compile/build time and per-run via RUBYOPT=--parser=prism|parse.y. The compiler bridge for Prism is prism_compile.c; the bridge for parse.y is the compile.c family.
Compiler and VM
compile.c walks the AST and emits a packed iseq containing:
- A flat array of bytecode opcodes and operands, defined in
insns.def. - A constant pool, line table, catch table (for
rescue/ensure), and metadata.
The VM is defined by:
vm_core.h— the master header forrb_vm_t,rb_thread_t,rb_control_frame_t, the call cache, and inline cache structures.vm.c,vm_eval.c,vm_method.c,vm_insnhelper.c— frame management, method dispatch, and instruction helpers.vm_exec.c(auto-generated frominsns.defviatool/ruby_vm/) — the actual dispatch loop. CRuby supports both aswitch-based dispatcher and a token-threaded dispatcher.
Method lookup uses inline caches (vm_callinfo.h) and shapes (shape.c, shape.h) so that instance variable access and method calls do not have to rewalk class hierarchies on every dispatch.
JIT compilers
Both JITs sit behind the VM and are optional:
- YJIT (
yjit/, entry inyjit.c) uses lazy basic-block versioning (LBBV). It compiles individual basic blocks of an iseq on first execution, specializes on observed types, and patches in branch stubs as new versions are needed. It targets x86_64 and arm64. - ZJIT (
zjit/, entry inzjit.c) uses a higher-level IR (HIR/LIR) and an offline-style optimizer. It is the more recent of the two and lives next to YJIT in the same Cargo workspace (Cargo.toml).
Both are statically linked Rust crates, configured in the top-level Cargo.toml workspace. See jits/index.md.
Memory and GC
Objects live in heap pages managed by the garbage collector. The GC interface is pluggable: gc/gc.h and gc/gc_impl.h define the contract; gc/default/ is the in-tree mark-sweep-compact implementation, and gc/mmtk/ binds to the MMTk research framework. The high-level API (gc.c) routes object allocation, marking, sweeping, and compaction to whichever implementation is selected at build time.
Object internals are organized around shapes (shape.c) — small immutable transition records that describe the layout of instance variables. Shapes let the VM and JIT specialize on object structure without inspecting class state on every read.
Concurrency
Three concurrency primitives coexist:
- Native threads (
thread.c,thread_pthread.c,thread_win32.c). All threads share one Global VM Lock (GVL) so only one thread runs Ruby code at a time, but blocking syscalls and explicitrb_thread_call_without_gvlregions release it. - Fibers (
cont.c,coroutine/). Cooperative coroutines whose stack switching is implemented in hand-written assembly undercoroutine/<arch>/Context.S. - Ractors (
ractor.c,ractor_sync.c). Actor-style isolated VMs that can run truly in parallel. Each Ractor has its own GVL.
Async IO uses Fiber Schedulers (scheduler.c) — a pluggable interface that lets gems like async plug their own event loops in.
Standard library and extensions
lib/— pure-Ruby standard library (e.g.,lib/optparse.rb,lib/uri/,lib/net/http.rb).ext/— bundled C extensions built withmkmf(e.g.,ext/socket/,ext/openssl/,ext/json/,ext/psych/,ext/digest/).gems/bundled_gems— gems that are pulled in at release time but maintained out-of-tree.
Many "default gems" are vendored — their authoritative sources live in standalone gem repositories and are synced via tool/sync_default_gems.rb.
Build system
The build is autoconf + make:
configure.ac→configure→Makefilecommon.mkis the platform-independent Makefile body.- Rust crates are built via
yjit/yjit.mkandzjit/zjit.mk, invoked from the main make. tool/m4/,tool/lrama/, and manytool/*.rbscripts generate code (the parser, instruction handlers, transcoding tables, etc.) at build time.
See build/index.md for the full pipeline.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.