Compilation pipeline

How the PHP compiler
turns code into machine code

Ten well-defined phases compile your PHP source into a standalone native binary. The compilation pipeline transforms PHP code into validated EIR, runs fixed-point IR optimization passes, then lowers it into optimized assembly for the selected target, then assembles and links it into a native executable. No Zend Engine, VM, interpreter, or opcode fallback is involved in the compiled path.

Aa
01

Lexer

Character-by-character scanning of PHP source into keywords, operators, literals, and identifiers.

{}
02

Parser

Pratt parser with binding powers builds a structured tree of expressions and statements.

>>
03

Resolver

Builds the compile-time autoload registry from Composer metadata and SPL rules, lowers per-file PHP magic constants, runs the ifdef/--define conditional pass, pre-scans statically-resolvable include targets for declarations, folds compile-time include path expressions, lowers include_once and require_once runtime guards, runs static autoload file insertion for referenced classes, then resolves namespaces and use imports to fully qualified names — merging multiple files into one AST.

T:
04

Type Checker

Infers and validates types for every variable and expression. Catches errors before assembly is emitted.

opt
05

Optimizer

Constant folding, constant propagation, control-flow pruning and normalization, and dead-code elimination with CFG-lite reachability. Conservative by design — rewrites only happen when the shape is statically provable.

EIR
06

EIR Lowering

The checked, optimized AST is lowered into elephc IR (EIR): a PHP-shaped SSA intermediate representation with explicit ownership, effects, and basic blocks. The module is validated before optimization and assembly.

opt
07

EIR Optimization

A module-level fixed-point pipeline interleaves a cross-function small-function inliner with per-function passes: identity arithmetic folding, peephole rewrites, per-block constant folding, dominance-based common-subexpression elimination, loop-invariant code motion, dead instruction elimination, dead store elimination, and branch simplification. Re-validates after every pass in debug/test builds.

reg
08

Register Allocation

A Poletto-Sarkar linear-scan allocator runs liveness analysis, builds live intervals, and assigns separate integer and float register pools so hot scalar values survive calls in callee-saved registers.

asm
09

EIR Codegen

The validated, register-allocated EIR module is translated to annotated assembly for the selected target: macOS ARM64, Linux ARM64, or Linux x86_64. Each line is commented explaining what and why.

ld
10

Link

The host `as` assembles to object code, then `ld` links into a standalone executable (Mach-O on macOS, ELF on Linux) with elephc runtime routines included.

.php tokens AST resolved AST typed AST optimized AST EIR optimized EIR registers .s .o binary

Code examples

Write PHP.
Compile to native.

150+ working PHP code examples ship with the compiler. Each one compiles to a standalone native binary and produces identical output to the PHP interpreter. From hello world to inheritance and exceptions — all compiled to native machine code.

hello.php
Hello World
1 <?php
2 echo "Hello, World!\n";
fibonacci.php
Fibonacci
1 <?php
2 function fib($n) {
3 if ($n <= 1) {
4 return $n;
5 }
6 return fib($n - 1) + fib($n - 2);
7 }
8
9 for ($i = 0; $i <= 20; $i++) {
10 echo fib($i) . "\n";
11 }
fizzbuzz.php
FizzBuzz
1 <?php
2 $i = 1;
3 while ($i <= 100) {
4 if ($i % 15 == 0) {
5 echo "FizzBuzz\n";
6 } elseif ($i % 3 == 0) {
7 echo "Fizz\n";
8 } else {
9 echo $i . "\n";
10 }
11 $i++;
12 }
primes.php
Prime Checker
1 <?php
2 function is_prime($n) {
3 if ($n <= 1) return false;
4 $i = 2;
5 while ($i * $i <= $n) {
6 if ($n % $i == 0)
7 return false;
8 $i++;
9 }
10 return true;
11 }
abstract-properties.php
Abstract Properties
1 <?php
2
3 abstract class Shape {
4 abstract public int $sides;
5 abstract public string $name;
6 public function describe() {
7 return $this->name . " has " . $this->sides . " sides";
8 }
9 }
10
11 class Triangle extends Shape {
12 public int $sides = 3;
13 public string $name = "triangle";
14 }
15
16 $shape = new Triangle();
17 echo $shape->describe() . " ";
pointers.php
Pointers
1 <?php
2 $x = 42;
3 $p = ptr($x);
4 echo ptr_get($p) . "\n";
5 ptr_set($p, 100);
6 $typed = ptr_cast<int>($p);
7 echo ($p === $typed ? "same" : "diff") . "\n";
destructor/main.php
Destructors
1 <?php
2
3 class ScopedLog {
4 private string $name;
5 public function __construct(string $name) {
6 $this->name = $name;
7 echo "open(" . $this->name . ") ";
8 }
9 public function __destruct() {
10 echo "close(" . $this->name . ") ";
11 }
12 }
13
14 function withResource(): void {
15 $job = new ScopedLog("job");
16 echo " ...working... ";
17 }
18
19 withResource();
20 $current = new ScopedLog("first");
21 $current = new ScopedLog("second");
22 echo "done ";
web-hello/main.php
Web Hello
1 <?php
2 echo "Hello from elephc-web!";

All 140+ included examples

hello abstract-properties advanced-functions anonymous-classes arithmetic array-access-exception-order array-parity arrays asymmetric-visibility assoc-arrays attributes autoload-reflection-stress binary-literals bitwise calendar callbacks case-insensitive-symbols catchable-access-errors classes cli-args cdylib closures concat constant-propagation constants constructor-promotion control-flow countdown cow data-stream date-json-regex datetime default-params destructor dir-wrapper disk-space dynamic-dispatch enums enum-methods error-control exceptions factorial ffi ffi-memory fibers fibonacci file-io file-modify file-stat final-classes fizzbuzz float-math foreach-ref formatted-stream-io fsockopen ftp-stream functions hashing generators guess-game gzip hostname hot-path http-stream https-stream ifdef include-namespace-fallback inheritance instanceof interfaces intersection-types ip-conversion iterable iterators json-basic json-exception json-flags json-jsonserializable large-by-ref-array-mutation logical magic-constants magic-methods math memory-stream multi-file namespaces nested-arrays never nullsafe-operator nullsafe-side-effects numeric-literals opendir paths pdo pdo-mysql pdo-pgsql phar-reader phar-write phar-writer php-wrapper pipe-operator pointers popen primes print-expression print_r-return property-hooks protocols readline references reflection relative-class-types sdl_audio sdl_framebuffer sdl_input sdl_window serialize services socket-pair spl-autoload spl-containers spl-decorators spl-delete-iteration-mutation spl-filesystem spl-foundation spl-storage static-classes static-properties strict-compare stream-blocking stream-chunk-size stream-copy stream-filter stream-filter-class stream-get-contents stream-introspection stream-lines stream-meta stream-notification stream-select stream-select-wrapper stream-wrapper-class streams-ext string-builder string-ops switch-match symlinks system-info strtotime-relative tcp-server timezone-info tls-client-cert traits type-narrowing type-ops typed-properties udg-socket udp-socket union-types unix-socket v017-trio var-export variadic variables web-demo web-framework web-hello web-request web-response web-router wrapper-metadata zval-pack

Documentation

Learn how a
PHP compiler works

12 guides covering every aspect of building a PHP compiler — from lexing and parsing, to type checking, code generation, and the ARM64 instruction set. No prior assembly or compiler knowledge required.

"Every line of Rust that emits ARM64 assembly is annotated with an inline comment explaining what it does and why."

From stack frame setup to syscall invocation, from integer-to-string conversion to array memory layout. If you've ever wondered what happens between echo "hello" and the CPU executing it, follow the code from src/codegen/ and read the comments.