There is a take I keep running into, usually from someone who has never shipped anything that had to keep running: if the model can write the code, who needs the language? Just tell the machine what you want. Compilers are a historical artifact, a translation layer we only needed because humans could not talk to silicon directly.

I read a long research write-up on this recently, and it pushed me the other way. Not “compilers survive AI.” Something stronger: AI is the reason compilers matter more now than they did five years ago. We just dropped a probabilistic engine into the middle of software creation, and the only thing in the stack that can tell it it is wrong, deterministically, every time, is the boring old toolchain everyone wants to declare obsolete.

We have had this exact argument before

In the 1950s, programming meant opcodes and memory addresses by hand. The people who did it well were an actual priesthood, and they knew it. When Backus and his team at IBM shipped Fortran, a good chunk of that priesthood hated it. The argument was not aesthetic, it was technical and it was reasonable: a generalized automatic tool will never produce code as tight as a skilled human writing assembly.

They were right, for a while. Early compilers emitted verbose, mediocre output. Then optimization passes got good, parity arrived, and the thing that actually mattered turned out to be the other number: a thousand lines of assembly became fifty statements. The economics flipped and the argument ended.

That is the pattern, and it has repeated enough that I trust it. Calculators did not delete mathematicians, they deleted manual arithmetic. Spreadsheets did not delete accountants, they turned them into modelers. Automation does not kill a discipline, it abstracts the implementation and raises the floor of what one person can do.

AI is the next layer on that same stack. Compilers automated human-code-to-machine-code. Models are automating intent-to-human-code. The part that is genuinely hard, decomposing a problem, choosing an architecture, knowing what “correct” even means here, has not moved an inch.

Natural language is a terrible specification format

This is the part the “prompts replace languages” crowd skips. Natural language is ambiguous by design. That is a feature when you are talking to a person who shares your context and a catastrophe when you are specifying behavior that has to be identical on every run, forever.

Ask a model for “the age difference between these two people” and you have smuggled in a dozen assumptions: which timezone, are we truncating, what happens on a leap day, what if one date is in the future. A programming language does not let you smuggle. It makes you say the thing, and then it does exactly that thing every time.

And if you actually tried to skip the middle and go from English straight to machine code, you would throw away three things I am not willing to lose:

Portability. My high-level code runs on x86, Apple Silicon, and ARM because the compiler knows the difference and I do not. Skip that layer and the model has to internalize the quirks of every chip on the market, and re-derive the compiler for each one.

Optimization. LLVM is decades of accumulated passes: loop unrolling, constant folding, dead code elimination, all operating on a structured IR where those transformations are provably safe. Asking a statistical model to reinvent that from scratch, per compile, is both absurdly expensive and exactly the kind of work models are worst at.

Debuggability. When something breaks at 2am, I want a stack trace with file names and line numbers. Without intermediate representations you get a raw memory fault and an address. Good luck feeding that back to anything, human or model.

So the stack is not collapsing. It is gaining a floor. Humans state intent in English, agents compile that into a real language, and the real compiler takes it from there.

Software 3.0, and why the context window is the new RAM

Karpathy’s framing for this is the cleanest I have seen. Software 1.0 is humans writing explicit instructions. Software 2.0 is humans curating datasets and letting backpropagation find the program, where the weights are the program. Software 3.0 is the model as the interpreter and the prompt as the interface.

He pushes the hardware metaphor further, and this is the bit that reframed things for me: the frozen weights are the CPU, and the context window is the RAM. Everything the program can reason about has to be resident in that window. Which means deciding what goes in it, which files, which tool outputs, which examples, stops being prompt fiddling and becomes an actual engineering discipline. It is memory management with a different name.

It also explains “jagged intelligence.” These things are statistical simulators shaped by their training distribution, so competence is not uniform. Land inside a dense region and the output is genuinely superhuman. Land outside it and the same model fails at something a junior would catch. The skill is knowing which side of that line you are standing on, which is why I want tools that augment me rather than autonomous agents I have to trust blindly.

The compiler as the thing that says no

Here is the asymmetry that makes all of this work. Models are measurably better at verifying than at generating. Call it the solver-verifier gap. One-shot generation is where the errors live; give the model an objective error message and it fixes things well.

So the useful pattern is not “generate and pray.” It is a loop. Generate, compile, feed the compiler’s complaints straight back into the context, patch, repeat until it is clean. The compiler is not the last step in the pipeline anymore. It is a participant, and it is the only participant that is never guessing.

Two refinements of this stood out.

Generative compilation fixes the timing problem. Normally the compiler only sees a finished file, so a mistake on line 12 has already had 300 lines of dependent, confidently wrong code built on top of it. Their approach uses a “sealor,” a lightweight syntax-guided transformation that closes off a partial program mid-generation into something an ordinary compiler will accept and diagnose, while tokens are still streaming. It is built so that a fragment which could still be completed validly is never wrongly rejected. The model finds out it is wrong while it is being wrong. They delegate the semantic checking to rustc, which is exactly where I would want this.

Clover goes further for the high-stakes case. The model emits three artifacts at once: the code, formal annotations (preconditions, postconditions, loop invariants), and a docstring. Then a deductive verifier, Dafny in their evaluation, cross-checks them against each other. If the code does not satisfy its own annotations, or the docstring is lying about what the code does, it goes back for repair. The clever move is reframing correctness, which is undecidable in general, as consistency between three artifacts, which is checkable. They report accepting up to 87% of correct programs while accepting zero adversarial incorrect ones, and the checker found six bad programs in a human-written benchmark everyone had been trusting. That is neuro-symbolic in the least hand-wavy sense I have seen: probabilistic generation, mathematical acceptance criteria.

The part that should worry you

If you take one number from this post, take this one. These models learned from public repositories, which means they learned our vulnerabilities along with our idioms. IOActive ran 27 models against 730 prompts across 27 languages, roughly 20,000 code samples checked against 219 vulnerability categories. Average security performance was 59%. Nearly a third of samples, 31.6%, were fully exploitable. No model produced clean output, and the best configuration still shipped 90 vulnerabilities. Infrastructure code was the worst of it, Dockerfiles and Terraform and CI pipelines landing between 70% and 97% vulnerable.

It gets worse when you iterate. One systematic analysis took secure baseline code in C and Java and ran rounds of AI refinement against it with no human in the loop, and found a 37.6% increase in critical vulnerabilities after just five iterations. Read that again: the refinement loop, the thing we all do reflexively, made the code less safe. It is a small study, ten baselines on GPT-4o, so hold it loosely. But it held across all four prompting strategies they tried, and one of those strategies was explicitly asking for security improvements. And static analysis often misses it, because AI-generated code blends idioms in ways heuristic scanners were never tuned for.

Memory-unsafe languages are the sharp edge here: buffer overflows, use-after-free, sloppy resource handling. Rust and Go take a real bite out of that class of bug. They do not help at all with the model quietly botching authentication or rolling its own crypto, which it will do in any language you give it.

None of this is an argument against using the tools. It is an argument for keeping the guardrails, and for the fact that “it compiles and the tests pass” was never the same sentence as “it is safe.”

Meanwhile, the compiler itself is being rebuilt

The traffic goes both ways. Compilers are full of hand-written heuristics approximating NP-hard problems: pass ordering, inlining, register allocation. Those heuristics are brittle and getting harder to maintain as hardware diversifies.

Google’s MLGO replaced some of them with reinforcement learning inside production LLVM. Inlining-for-size is a sequential decision problem over a huge call graph where every choice reshapes the next one, which is a textbook RL setup, and it bought up to 7% size reduction against -Oz, which is the tuned heuristic it was replacing. A learned register allocation eviction policy got 0.3% to 1.5% QPS across internal datacenter workloads. Small percentages, enormous denominators.

There is also good empirical evidence that the human heuristics were flying blind. A study decomposing the LLVM -O3 pipeline took 84,750 measurements across 113 cumulative prefixes of the pipeline on 30 PolyBench kernels, and found optimization is strongly non-monotone: somewhere between 6.6% and 9.7% of pass transitions make execution slower. Phase interference, where one pass destroys the preconditions another needed, accounts for an estimated 46.35% of lost achievable speedup. My favorite finding is that the final -O3 configuration is Pareto-dominated on size and speed for 29 of those 30 kernels. The default everyone ships is not on the frontier for almost anything. Nobody hand-writes their way out of an interaction space that ugly.

And the infrastructure underneath is new too. MLIR gave us a compiler framework built out of composable dialects instead of one fixed IR, which is what lets PyTorch lower a graph through linear algebra abstractions before it ever sees LLVM IR. Mojo targets MLIR directly to get Python ergonomics with systems-language performance. Triton lets researchers write GPU kernels without a CUDA PhD. That is a whole compiler generation designed around tensors and heterogeneous hardware rather than scalar code on a CPU.

Languages are starting to optimize for a reader who is not human

This is the strangest thread in the whole thing, and the one I am least sure about.

Language design has always traded human ergonomics against machine efficiency. Now there is a third axis: how well does a model generate and refactor this thing. Every semicolon, brace, and type annotation costs tokens, and tokens are context budget and money. A 2x verbosity difference is a 2x inference bill.

And the tradeoff is genuinely awkward, roughly like this. Models are most fluent in Python, JavaScript, and TypeScript, because that is what the internet is made of, and those are exactly the languages whose dynamic typing gives the verification loop the least to push back with. Java, C#, and Go hand the compiler plenty to work with and charge you in tokens for every line of it. Rust and Haskell offer the strongest guarantees once they compile, and are the ones models are worst at, because borrow checking and heavy functional abstraction are thin in the training data and hard to pattern-match your way through.

Sit with that shape for a second. Terse saves context but starves the compiler. Verbose feeds the compiler but eats the window. The safest languages are the ones the models handle worst. There is no free square on this board.

People are already building for this, though I would not get attached to any of them yet. SimPy is the most serious of the three: a research grammar that mechanically strips redundancy out of Python while keeping an identical AST, so you can round-trip between them. The savings are real but modest, 13.5% fewer tokens for CodeLlama and 10.4% for GPT-4. MIMERCode goes the other way and optimizes for security rather than size, with contracts that reject bad data at the boundary and sandboxed I/O so a generated program simply cannot import os and go wandering through your filesystem. GlyphLang swaps keywords for symbols to cut tokens, claiming 23% fewer than FastAPI and 57% fewer than Java, though those are the project’s own numbers on its own benchmarks and it is a very young project with no track record behind it. The obvious risk with the symbol-heavy approach is semantic collision, where a character like @ drags in Python decorators and shell globs and email addresses all at once and the model gets confused by its own compression.

I do not think any of these win. SimPy is a paper, MIMERCode and GlyphLang are early projects, and “AI-friendly syntax” is exactly the kind of idea that attracts more launches than survivors. But I would not bet against the pressure being real, and I did not expect “how cheap is this to tokenize” to become a language design constraint in my career.

What the machine still cannot do

Which brings me to Claude’s C Compiler, which Anthropic published earlier this year and which is the most concrete data point in this whole argument.

The numbers first. Around 100,000 lines of Rust, clean-room and dependency-free, written by sixteen Claude agents running in parallel with a researcher who set up the test harness and then mostly walked away. Roughly 2,000 sessions over two weeks, two billion input tokens, just under $20,000. It emits x86, ARM, and RISC-V, and it compiles a Linux 6.9 kernel that actually boots on all three. It passes 99% of most compiler test suites, including GCC’s torture tests.

A compiler is a good benchmark precisely because it does not tolerate “mostly working.” A web app with a UI bug annoys people. A compiler with one bad transformation pass silently corrupts everything downstream of it. And compilers happen to be unusually legible to a model: layered abstractions, consistent naming, discrete composable passes, deterministic feedback on every change. If AI is going to do systems work, this is the friendliest hard problem available. Note the testing approach, too, because it is the closed loop from earlier taken to its conclusion: late in the project they used GCC as an oracle, compiling random subsets of the kernel with both and diffing the output. A deterministic ground truth, queried a few million times.

So it worked. The interesting part is how. Chris Lattner took it apart, which is a fun turn given he is also the person behind LLVM, MLIR, and Mojo. His verdict: modern LLMs are “extraordinarily powerful distribution followers.” The model did not invent an architecture. It rebuilt the classic three-phase one, frontend to IR to multi-target backend, because that is the accumulated consensus of decades of compiler engineering and the model had thoroughly absorbed it. Every design choice reflects well-established practice. None of them reflect an idea nobody had written down before.

And the shortcuts are telling. The code generator reparses assembly text instead of carrying an IR through. System headers are hardcoded rather than parsed. Error recovery in the parser is poor. Lattner’s conclusion is that it will not generalize much past its own test suite.

Sit with that one, because it cuts against the rest of this post. The thing scored 99% and still may not generalize, and the reason is that the tests were the ground truth. Point a tireless optimizer at a fixed oracle and it will satisfy the oracle. Everything the oracle did not ask about is a coin flip. The deterministic verifier is still the right idea, it is just not a substitute for someone deciding what should have been verified.

That is the honest boundary right now. Unmatched at synthesizing and applying accumulated human knowledge, at a scale and stamina no team of ours matches. Still dependent on us for the conceptual leap, and for knowing what the test suite forgot to ask.

So, keep the compiler

The thing I keep coming back to is that we have accidentally rebuilt the software stack around a component that is fast, broadly capable, and occasionally confidently wrong. Every engineering discipline that has dealt with a component like that solved it the same way: pair it with something slow, narrow, and never wrong, and let the second thing have the final say.

That is the compiler now. Not a translator we tolerate on the way to a binary, but the deterministic arbiter that keeps the probabilistic part anchored to reality. The type checker, the borrow checker, the test suite, the static analyzer: every one of them just got promoted.

Learn your tools. Keep the loop closed. Let the machine write the syntax, and let the compiler be the one that says no.


This post was written with the help of AI (Claude by Anthropic).