I'm not sure, haven't looked at the codebases of old compilers in a long time. Definitely a lot of the language is pretty amenable to it, especially if you have unstructured jumps for e.g. the for advancement statement. I had a distinct feeling while writing the compiler every time I added a new feature that "wow, the semantics work exactly how I'd like them to for ease of implementation."
Compare that to, say, Rust, which would be pretty painful to single-pass compile with all the non-local behavior around traits.
What you are saying is true for a naive C compiler.
Once you want to optimize or analyse, things become more complicated.
> Compare that to, say, Rust, which would be pretty painful to single-pass compile with all the non-local behavior around traits.
Type inference also spans a whole function, so you can't do it in a single pass through the code. (But it's still tamer than in eg Haskell, where type inference considers your whole program.)
Compare that to, say, Rust, which would be pretty painful to single-pass compile with all the non-local behavior around traits.