Also C compilers can do a lot of micro optimizations that are not trivial, so compiling something to C or maybe LLIR seems like a better choice.
Also C compilers can do a lot of micro optimizations that are not trivial, so compiling something to C or maybe LLIR seems like a better choice.
The first issue is the impedance mismatch between the original language semantics and what is possible to represent in C.
Two examples are tail calls and continuations, that are hard to get right at C level, either requiring compiler extensions, longjmp/setjmp tricks, or a little bit of assembly.
Then there is the whole issue with undefined behaviour in C, introducing bugs that didn't exist on the original code.
Even if the generated code is clean today regarding UB, you cannot ensure that a new C compiler version won't be introducing such issues.
Having said this, there are languages like Eiffel and Nim, whose main backend relies on C compilers, instead of compiling directly to pure native code.
Skipping Objective-C and C++ here, because although they started as C pre-processors, their code was to be an extension to C and so their semantics also imply C semantics.
Compiling to some kind of IR, like LLIR is more sane.
Then you can do whatever you want with it, generate code in another programming language, machine code, or a plain interpreter for bootstrapping purposes.
> Even if the generated code is clean today regarding UB, you cannot ensure that a new C compiler version won't be introducing such issues.
This is not correct. Undefined behaviour is specified in the standard, and a good compiler-to-C will always generate UB-free code.
If your code avoids UB behavior as specified by the standard it will run in the same way when compiled with any standard-compliant compiler.
What gets documented on the standard is what compiler vendors that bother to seat at ANSI C meetings agree to document as such.
By no means does the standard forbid compiler vendors to take advantage of situations not yet documented as such.
Eventually new cases of UB might get added to the standard.
The current set of about 200 scenarios of UB in C11 weren't all there in C89.
Another example, is that you can have code that is perfectly valid, UB free, but thanks to PGO and code re-writting rules on the optimizer stage, gets rewritten with UB side effects at a later stage in the optimizer pipeline.