9 min read ·
The only code generator I trust
The best code generator ever built has no chat window. It writes almost every instruction your CPU runs, and nobody has ever asked to review its pull requests.
Two weeks ago, on Alphabet's earnings call, Sundar Pichai said that "more than a quarter of all new code at Google is generated by AI, then reviewed and accepted by engineers." That's the exact sentence from his remarks. The headlines kept the first half. I keep rereading the second half.
Reviewed and accepted. By engineers.
Now think about the code nobody reviews. Nobody opened a pull request for the assembly your laptop ran today. Nobody went on an earnings call to announce that compilers wrote nearly all of Google's machine code, because that stopped being news before most of us were born.
That missing review step is the argument. We already have a code generator we trust without reading its output. So what did it do to earn that, and how far is the new one from doing the same?
Trust was not free
When IBM shipped FORTRAN for the 704 in 1957, programmers did not trust it either. IBM's own history says the team had to beat skeptics who insisted "that a program compiled from a high-level language could never be as efficient as one that was handcrafted directly using numerical codes." The answer was an optimizing compiler whose output ran nearly as fast as code written by hand, over and over, until arguing stopped being worth it.
Hold on to that. The compiler earned trust by being boring in the same way, every time, for decades.
Inside, a compiler is a stack of small translators, and each one hands a precise result to the next.
The lexer cuts your text into tokens, the way you'd split a sentence into words: sum, =, a, +, b. The parser checks those tokens against the grammar and builds a syntax tree, a little family tree where + is the parent and a and b are its children. Then come the rude questions. Was sum ever declared? Why does a function that takes two arguments get called with three? What survives gets translated into an intermediate form, a plainer language that no human writes by hand, and the optimizer rewrites it into something cheaper that means the same thing. Last, the code generator picks real instructions for a real CPU and decides which values live in its registers, the handful of tiny, fast slots inside the chip.
Most of the error messages you have ever cursed at came from one of those steps. Notice what they have in common. They stop. They give you a file name and a line number. They don't guess what you meant and hand you a program anyway.
Watch it work
Here's a program that adds two numbers:
int main() { int a = 5; int b = 10; int sum = a + b; return sum; }
Compile it with Clang, one of the two big open-source C++ compilers, for an Intel or AMD chip with no optimization (-O0). You get roughly what you wrote. Store 5. Store 10. Load one, add the other, store the result, load it again, return it. On my Clang, that's eleven instructions of honest busywork.
Turn the optimizer on with -O2 and you get this:
main:
mov eax, 15
ret
The variables are gone. So is the addition. The compiler saw that a and b never change, worked out 5 + 10 while compiling, threw away the variables nothing reads, and left one instruction that puts 15 in the register where a return value goes.
I have heard this called inlining. I have called it that myself. It isn't. There is no function call to inline. It's constant propagation (replace a variable with the value you know it holds) and constant folding (do the arithmetic now instead of at runtime). You can watch this happen yourself on Compiler Explorer, the best free education in this field.
Why is the compiler allowed to delete my code? Because the C++ standard has the as-if rule. The compiler may make, as cppreference puts it, "any and all code transformations that do not change the observable behavior of the program." My program returns 15. It still returns 15. Everything else was my business, not the CPU's.
A contract with the edges written down
My favorite part is the one people learn about the hard way:
int f(int x) { return x + 1 > x; }
At -O2, Clang turns that into mov eax, 1 and ret. It always returns true. But if x is the biggest int there is, adding one overflows, so how can that be right?
Because an int that overflows is undefined behavior. The standard says, in writing, that it places no requirements at all on a program that does this. So the compiler is free to assume it never happens. In that world, x + 1 is always bigger than x. The compiler kept the contract. You broke it.
That sounds hostile until you compare it with a generator whose promises have no edges at all. The compiler's edges are published. You can read exactly where the guarantees stop.
Boring on purpose
Two more properties, neither exciting enough for a keynote.
Same input, same output. The Reproducible Builds project defines the goal plainly: given the same source, environment and build instructions, "any party can recreate bit-by-bit identical copies" of the result. Their docs call timestamps the biggest source of trouble, because build tools love to stamp the current time into the output. People have spent years hunting those leaks out of packages, one at a time, so that anyone can rebuild one and check it byte for byte against the download.
Tested by the whole planet. Every program anyone ships is a test case for the compiler that built it, and researchers write programs just to break compilers. A team at Utah built a tool called Csmith that generates random, valid C programs and compares what different compilers do with them. Over three years they reported more than 325 previously unknown bugs to compiler developers, 79 of them in GCC and 202 in LLVM, the project behind Clang. Their abstract has one line every engineer should memorize: "Every compiler we tested was found to crash and also to silently generate wrong code when presented with valid input."
So compilers are wrong sometimes. The difference is what happens next. Each of those bugs arrived as a small program that reproduced it, and most of the GCC and LLVM bugs were fixed. Then there was CompCert, a compiler whose optimizations come with mathematical proofs. Csmith found bugs in its unproved front end too. In the verified middle, where the optimizations live, the paper says it found no wrong-code errors at all, after about six CPU-years of trying.
Even this trust has an edge. In his Turing Award lecture, published in 1984 as Reflections on Trusting Trust, Ken Thompson showed how a compiler could plant a backdoor that survives a careful read of its own source code. His moral, in his words: "You can't trust code that you did not totally create yourself." Reproducible builds don't beat his trick. They do something smaller and useful: when a stranger rebuilds the same bytes from the same source, you stop taking one builder's word for it.
Compiler cosplay
Now the new generators. In Stack Overflow's 2024 developer survey, 76% of developers said they use or plan to use AI tools. Only 43% said they feel good about the accuracy.
Picture a compiler that 43% of its users trusted. You wouldn't call it a compiler. You'd call it a first draft. You'd review everything it produced before you shipped it, which, by Pichai's own account, is exactly what Google's engineers do.
I use these assistants most days, and they save me real typing. What I don't buy is the bolder pitch, the one Andrej Karpathy squeezed into a single line: "The hottest new programming language is English." Its fans take the next step: the model is its compiler, and reading the output will soon feel as quaint as reading assembly.
That's compiler cosplay. A compiler maps one specified language onto another, and it refuses when it can't. A model turns an unspecified wish into plausible text. When it has to guess, it usually doesn't tell you. Ask for the same function twice and you can get two different functions, like asking the same uncle twice for directions to the wedding. There's no standard for a prompt. There's no as-if rule. Nobody can tell you where its promises stop.
The worst part is the message it sends to people starting out: that the layers under the prompt are now someone else's problem. It has that backwards. When generated code is wrong, what catches it the same way every time is the boring layer under it: the type checker, the tests, the warnings.
Here's a function with a bug in it:
int g(int x) { int s; if (x) s = 1; return s; }
Turn on the warnings (-Wall -Wextra) and Clang tells you that the variable may be used before it was ever given a value: "variable 's' is used uninitialized whenever 'if' condition is false", points at the line, and suggests the fix. Same answer every time, for free, in a fraction of a second. -Wall -Wextra is the cheapest reviewer you will ever hire. It only helps someone who understands the complaint.
Until a model makes the promises a compiler makes, the second half of Pichai's sentence is the honest half.
Learn what your compiler does with your code, or stay a tourist in your own program.
