Twitxy Saved

amlug.eth

Verified account This account is verified. Learn more

@realamlug

Jesus follower. Husband. Dad of 3 boys, 1 girl. Building Perry - TypeScript compiled to native binaries. No Electron. 馃嚛馃嚜
Breckerfeld, Germany perryts.com Joined January 2021
1.8K Following 2.8K Followers 6.9K Posts

Latest posts 5

Pinned post

amlug.eth Verified account @realamlug
Perry Update: Compiling JavaScript/TypeScript is complicated!

How Perry compiles JavaScript and TypeScript to a native binary, and why the hard part isn't the code generation.

The pipeline (image):
1. SWC parses your .js/.ts and its dependencies from node_modules.
2. The AST becomes perry's HIR, a high-level intermediate representation.
3. HIR transforms: closure conversion, async/await lowering, inlining.
4. LLVM lowers the HIR to machine code for the target.
5. The system linker links the object files with perry's runtime (including a generational GC) and the native libraries the program needs.

The result is one self-contained executable. No JIT, no VM warm-up.

The hard part sits between steps 2 and 4. At every o.x and every f(a), the compiler must decide before the program runs: one load at a fixed offset, or the general property lookup (find the layout, search it, walk the prototype chain). A JIT watches the program and guesses; an AOT compiler has to know.

TypeScript types can't decide it. They can be wrong (as, any, stale .d.ts); they say what a value may be rather than where its fields live, and most npm code ships without them. Trust a wrong type, and you read the wrong slot. So the rule is: types pick the lowering, shapes prove the semantics. This is also something no other JS/TS AOT compiler does yet.

That's what perry's whole-program facts do (the lower half of the image):
- Per-unit facts: every module is cut into units, cycles of declarations, and each unit is analysed for object layouts and for what its functions read, keep, call, and change.
- Contracts: each unit publishes what others may rely on, e.g., "Circle has r at slot 0, and nothing adds keys to it".
- Census: one program-wide check of which layouts anything can break (delete, computed-key writes, stores through unknown values).
- Rounds: affected units re-solve until nothing changes.

Proven: p.x is one load at a fixed offset, and c.area() is a direct call behind a single class check. Not proven: the general lookup, which is always correct. Missing facts cost speed, never correctness.

Where it stands (2026-10-30), on the TypeScript compiler's own source: 20,028 sites proven per unit, up from 390 before 2026-10-25. tsc's checker serves as an independent oracle, never as input: its types pin down 42,313 sites, leaving a 22,865-site worklist. Still open: unit edges (Richards: 37 sites per unit vs 1,673 whole-program), stores into built-in prototypes, compile cost.

What compiles and matches node: the AWFY benchmarks, the TypeScript compiler, commander, fastify, qs, yargs, marked, effect and the 22-program js-compiled suite.

Small programs, like benchmarks, etc., work out exactly the way we need them to. The difficult piece is compiling entire programs, including their NPM dependencies. This is still ongoing, but progress is going well. :)
amlug.eth Verified account @realamlug
This one hit a nerve lol.

The point was simply: choosing a "native/fast" language doesn鈥檛 make your software fast.

A badly written C++/Rust app can easily lose to a well-written Node app. Architecture, algorithms, and I/O matter far more than the language for 99% of what we build. Not to forget the compiler. ;)

Yes, lower-level languages give the compiler more to work with, so the ceiling is higher. But most apps aren鈥檛 drivers or game engines. They never get anywhere near that ceiling.
amlug.eth Verified account @realamlug 2d
Rust is not faster than JavaScript!

Rust is also not faster than C++.

The compiled binary code might be faster than the compiled binary code of either of the others. But that's a compiler question, not a language question.

Some languages make it easier to compile to performant code than others, but the trade-off is in the language itself.

Don't confuse language with execution.
View quoted post on X
10 replies 1 reposts 32 likes 4.8K views View on X
amlug.eth Verified account @realamlug
Rust is not faster than JavaScript!

Rust is also not faster than C++.

The compiled binary code might be faster than the compiled binary code of either of the others. But that's a compiler question, not a language question.

Some languages make it easier to compile to performant code than others, but the trade-off is in the language itself.

Don't confuse language with execution.
76 replies 14 reposts 332 likes 92K views View on X
amlug.eth Verified account @realamlug
To develop Perry I develop a lot in Rust.

So, to have a good dogfooding project I developed tuichess.dev

Two interesting things:

1. It's freaking fun to develop games in Typescript. Development is fast and AI is really good. Rust development is so slow compared to that!

2. Perry will provide an easy way to convert three.js games into **fully native games**! More about that soon!
tuichess.dev tuichess 路 chess in your terminal Play chess beside Claude Code, opencode or Codex, or in any terminal: a Perry-native engine and online games with clocks and high scores.