> mainframe language environments and .NET have long supported C and C++
I should have been more clear: I'm not saying you can't define an IR language as a target for a C compiler, and then have that IR run on various platforms. As you say, that's already been done with solutions like C++/CLI, and compiling C++ to JavaScript or to WebAssembly. My point is that this isn't the same as what Java bytecode does for Java.
When you compile Java to Java bytecode, there's no platform-sensitivity in that compilation step. You can run javac on Windows, and on FreeBSD, and you'll get identical .class files from both.
C is importantly different from Java, in two regards:
1. C permits platform-specific use of the preprocessor, so that the programmer can for instance activate a specially tailored Windows-specific version of a function if and only if the target platform is 64-bit Windows. (I'm not fond of the term conditional compilation to describe this, but it seems to have stuck.)
2. Properties of the underlying platform are revealed to the programmer at compile-time, such as in sizeof(long)
If you treat your IR as the compilation target, you have to commit to a virtual platform that might not match the underlying platform. You'll need to decide a value for sizeof(long), and the size of pointer types, etc. You can do this, sure enough, but it's presumably going to make things awkward and introduce a possible performance penalty.
More importantly though, it's also going to break the way C programmers tailor their programs for different operating systems, how they cope with the availability of different features and optional libraries, etc. Consider the build-specific details that tend to be handled by autotools. Platform-specific preprocessor decisions could also happen in any header file that you rely on.
This means it fails to act as a universal portable intermediate representation for C programs. A single universal IR blob isn't going to cope with something like this:
void initialize_graphics() {
#ifdef USE_DIRECT3D
initialize_direct3d();
#else
initialize_vulkan();
#endif
}
Depending on the platform, the function body changes completely. You could single out the IR as a distinct platform:
void initialize_graphics() {
#if defined WEBASSEMBLY_BUILD
initialize_webassembly_graphics();
#elif defined USE_DIRECT3D
initialize_direct3d();
#else
initialize_vulkan();
#endif
}
Java, by design, is unable to express compile-time decisions of this sort. Compilation from
.java to
.class isn't 'lossy' the way compiling C is.
To put all this more succinctly: with Java you get the build, with C you get a build. With Java, a .class file a function of a Java source file, whereas with C, a compiled object file is a function of both a C compilation unit and the platform.
> The only reason why LLVM bitcode doesn't do this is political, meaning the LLVM designers don't want to follow down this path.
On top of what I've mentioned, I doubt they want to be tied to perfect backward-compatibility for LLVM bitcode. Not sure that counts as political though.
Of course, Google already gave this a go, with PNaCl.