This missing separation is even a problem for writing tests in GCC: Often it would be quite easy to write a small program in GIMPLE-IR (or RTL) and test your optimization pass against it. But you can't write GIMPLE-IR (it needs to go through GENERIC all the time), so you need to write a C file that is then translated into the IR you want to test again. This is sometimes not so easy with all that different optimization passes that run before your pass and hopefully that doesn't change in the future either.... There was some work on this (I don't know about the current status) but almost all of GCC's tests are still integration tests against C files (I think they addressed this by extending the C-front end to be able to parse functions marked with __gimple as Gimple-IR). Also GIMPLE didn't start as a separate IR. It started as a more restricted tree-structure. Even today operands are still tree's and optimization passes are called tree-<something>.c.
With LLVM I can write a toy compiler that outputs LLVM IR with printf and then just pipe it into llc to compile it. That's pretty cool. I didn't want to imply that GCC has NO separation between IRs but the whole story is much messier compared to LLVM. Which is actually fine, LLVM is much younger than GCC and could learn a lot from it. And GCC is still pretty great in the use cases it was intended for.