E.g. Clang front end reads C code and emits LLVM IR. The LLVM backend (llvm-as) then reads the IR and generates target machine code.
IE LLVM does not make C magically portable, because C requires and creates target-specific information (for example sizeof).
To give a simple example, you can't take:
int *foo;
printf("%d\n", sizeof(foo))
Compile it on a 16 bit platform to llvm bitcode
and then run compile/run the llvm bitcode on a 64 bit platform, and get the right answer for that platform, and llvm can't solve this.The long story on this one is that it's basically not possible to even just move this stuff to dynamic evaluation in a frontend because you can depend on target specific things in the preprocessor, IIRC. Otherwise, you could, but it'd generate super-slow code unless you built llvm intrinsics for all these things, and then lowered them once you had target specific info.
I believe TeNDRA and friends could do this at one point, but i might be misremembering (i've deliberately blocked most of my knowledge of ANDF)
Well, unless you go the whole way and interpret the language itself like CINT :)
The IR code itself is "portable", but the C to IR compiler is using target specific information?
(I've just been learning about assemblers and compilers, still trying to wrap my mind around this stuff. Would there be much educational value in writing IR code by hand?)
Right. Consider how you'd compile something as simple as printf("%d", (int)sizeof(int));
However, Clang does not do this and emits a constant integer for sizeof() operations (according to my quick and dirty test).