Compilation pipeline

How the PHP compiler
turns code into machine code

Twelve well-defined phases compile your PHP or LFC source into native output. 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 native output. No Zend Engine, VM, interpreter, or opcode fallback is involved in the compiled path.

.?
01

Classify

Every physical source file is classified independently from its path: `.lfc` starts in code mode with no `<?php` tags and always enables elephc extensions, while every other suffix keeps tagged-PHP behavior. With `--strict-php`, each PHP-mode AST is then audited for elephc-only constructs before any later pass runs.

Aa
02

Lexer

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

{}
03

Parser

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

>>
04

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. Function-argument introspection (func_num_args, func_get_args) is desugared and the OPcache script manifest is baked before optimization begins.

T:
05

Type Checker

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

opt
06

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.

WPO
07

Reachability

Drops unreachable functions, classes, methods, and injected prelude declarations before EIR lowering. Dynamic calls, eval, unserialize, and Reflection widen the retained surface conservatively.

EIR
08

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
09

EIR Optimization

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

reg
10

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
11

EIR Codegen

The validated, register-allocated EIR module is translated to annotated assembly for macOS ARM64, Linux ARM64, Linux x86_64, iOS ARM64, or the iOS ARM64 Simulator. Each line is commented explaining what and why, and a v2 source map is written alongside the `.s` file.

ld
12

Link

The host assembler produces object code. Locked managed native packages, the cached runtime object, and bridge inputs become a typed link plan, then the linker produces a standalone executable, shared library, or static library for the selected target.

.php / .lfc tokens AST resolved AST typed AST optimized AST reachable AST EIR optimized EIR registers .s .o native output

Code examples

Write PHP.
Compile to native.

200+ 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!";
eval/main.php
Eval
1 <?php
2 $score = 10;
3
4 eval('$score = $score + 5;');
5
6 echo "score=" . $score . "\n";
lfc/main.lfc
Tagless LFC
1 buffer<int> $values = buffer_new<int>(3);
2 $values[0] = 10;
3 $values[1] = 20;
4 $values[2] = 12;
5
6 echo "Tagless LFC result: ", $values[0] + $values[1] + $values[2], "\n";
7 buffer_free($values);

All 200+ included examples

hello hello-preg abstract-properties advanced-functions anonymous-classes arithmetic array-access-exception-order array-internal-pointer array-parity arrays asymmetric-visibility assoc-arrays attributes autoload-reflection-stress bcmath binary-literals bitwise calendar callbacks case-insensitive-symbols catchable-access-errors classes cli-args cdylib closures concat constant-propagation constants constructor-promotion control-flow control-flow-normalization countdown cow curl-callbacks curl-get data-stream date-json-regex datetime declaration-reachability declare-directives default-params destructor dir-wrapper disk-space dynamic-dispatch enums enum-methods environment error-control eval eval-globals eval_regex exceptions extension_loaded 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 generators get-object-vars guess-game gzip hashing hostname hot-path http-stream https-stream iconv ifdef image_basics image_cairo image_cairo_procedural image_exif image_gmagick image_imagick image_transform include-namespace-fallback inheritance instanceof interfaces intersection-types ios-device-probe ip-conversion iterable iterators json-basic json-exception json-flags json-jsonserializable large-by-ref-array-mutation lfc local-retype logical magic-constants magic-methods math memory-stream monitoring multi-file mysqli-crud namespaces nested-arrays never nullsafe-operator nullsafe-side-effects numeric-fast-paths numeric-literals opcache_get_configuration opendir openssl_crypt output-buffering parse-url paths pcntl pcntl-daemon pdo pdo-cubrid pdo-dblib pdo-firebird pdo-ibm pdo-informix pdo-mysql pdo-oci pdo-odbc pdo-pgsql pdo-sqlsrv 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 strict-php 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 swiftui-view-protocol 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 web-session web-session-trans-sid web-session-upload wrapper-metadata xml zval-pack

Documentation

Learn how a
PHP compiler works

13 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.