7 ms·
I've been wondering lately if the modern compilers should all be using C as the intermediate language (or some language-specific code optimization opportunities
by Koshkin 7y ago
I've been wondering lately if the modern compilers should all be using C as the intermediate language (or some language-specific code optimization opportunities could be lost if they do that).
- edwintorok 7y agoThe semantics of C aren't very well defined, there is a lot of ambiguity in the form of undefined and implementation defined behaviour. This ambiguity is often needed to build an efficient optimizing compiler. When you have a higher level language with more accurately defined semantics, running it all through C would risk introducing undefined behaviour. With an IR you can control and define the semantics more closely to what your language needs.
- ansible 7y agoI've got to wonder if any of the existing intermediate representations would be appropriate with other programming languages.
- steveklabnik 7y agoThis is true to varying degrees, you could say that LLVM-IR and Java bytecode are two examples of this in action.
- jfkebwjsbx 7y ago> When you have a higher level language with more accurately defined semantics, running it all through C would risk introducing undefined behaviour. No, it wouldn't. When you target C you need to write a proper backend for its abstract machine, rather than naively rewriting code, of course. The C abstract machine is a fine IR, specially the later editions of the standard.
- epage 7y agoWhile a C backend is great for compatibility, is it a sufficient IL to express everything? For example, Rust has some extra guarantees with aliasing that I'm unsure if C or C extensions support yet that could offer greater optimizations (currently not fully being used due to bugs in the LLVM backend).
- bregma 7y agoModern compilers generally have a language-specific front and that generates an intermediate representation of the program logic, which is then transformed into an abstract representation (such as a single static analysis tree) for optimization. That is then transformed into an abstract machine description language, which gets further transformed by the back end into concrete machine instructions or assembly code. Outside of the language-specific front end, compilers generally have no knowledge of the programming language itself. There is no technical advantage to transforming Rust into C when it comes to the middle and back ends, which form the bulk of the compiler. There are no language-specific optimization opportunities. There are, of course, restrictions on what you can do in some languages that eliminate optimization opportunities, but you're not suddenly going to be able to take advantage of those opportunities by transforming your code into a langue that lacks the restrictions, because then you change the semantics of your code.
- jfkebwjsbx 7y ago> There is no technical advantage to transforming Rust into C There is a key one: the ability to use any C compiler out there (including proprietary ones). This allows you to target all platforms out there.
- pjmlp 7y agoA dumb interpreter for the IR as bootstraping stage is a better alternative. Plus very few platforms have only support for C and nothing else, unless we are speaking about esoteric embedded CPUs.
- jfkebwjsbx 7y agoOf course we are talking about embedded CPUs. And no, they are not "esoteric". They are everywhere around you. Then you can also add custom CPUs and systems, FPGAs, etc. Those are way more rare, but still something people use daily in some industries.
- steveklabnik 7y agoWe have an interpreter for MIR. It isn't fast enough.
- steveklabnik 7y agoIt's a sad thing that you've been downvoted for posting a thought. Others have already said the drawbacks of this idea, but there are also pros.
- mratsim 7y agoIndeed. Time-to-market is an obvious one and esoteric platform support, and maybe also debuggability.
- Ididntdothis 7y agoThat’s how C++ started out but as far as I know this had lots of limitations in terms of optimization so they started writing native C++ compilers.
- mratsim 7y agoI would be surprised if optimization was the actual goal. C++ is not faster than C.
- Ididntdothis 7y agoIt’s not faster but it’s also not much slower. I think some features of C++ Would be hard to optimize by a compiler that doesn’t understand them so a C++ to C compiler may produce slow or bloated code.
- mratsim 7y agoIn embedded development C++ is often frowned upon because in the past people used C++ features that bloated the code. Regarding features that may not be properly optimized, there are exceptions and move semantics I guess, but exceptions are often avoided like the plague anyway and code can be refactored to get the same effect as move semantics. Am I missing something?
- pjmlp 7y agoNot only does C++ provide features that straight C optimizers won't be able to match like templates and constexpr, C++ shares a common subset with C, and libc all major C compilers is actually written in C++ with extern "C" entry points nowadays. This "C is faster than C++" is a bit dated by now.
- mratsim 7y agoI didn't say C is faster than C++, I said C++ is not faster than C and so speed is probably not the main driver behind implementing a C++-->Assembly pipeline without intermediate C.
- stephencanon 7y agoIf you're trying to implement a language with substantially different semantics from C (e.g. a substantially different memory model, or without UB) the semantics of C make it really unsuitable as an IR. You can't use C's casts (undef for out of range float -> int conversions, for example), arithmetic (undef for signed overflow), or shift operators (implementation-defined behavior for signed right shifts, undefined behavior for left shifts into the signbit or shift counts not in [0, n)). You can work around these by defining functions with the semantics that your language needs, but they get gross pretty quickly (they are both much more verbose and more error-prone than having an IR with the semantics you really want, and they require optimizer heroics to reassemble them into the instructions you really want to generate). Alternatively, you can use intrinsics or compiler builtins, but then you're effectively locking yourself to a single backed anyway, and might as well use its IR. The issues around memory models (especially aliasing, but also support for unaligned access, dynamic layouts, etc) are worse. Even LLVM IR is too tightly coupled to the semantics of C and C++ to be easily usable as a really generic IR for arbitrary languages (Rust, Swift, and the new Fortran front end have all had some struggles with this, and they're more C-like than most languages). C is much worse in this regard.
- raphlinus 7y agoI agree 99.44%. The behavior of shift operations on signed integers will be fixed in C++20 and C2x, as part of the effort to require twos complement representation. It is a massive potential source of UB in currently standardized C and C++. All the other problems listed remain. [1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r1.html http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p090... [2]: http://eel.is/c++draft/expr.shift#2 http://eel.is/c++draft/expr.shift#2
- stephencanon 7y agoEven after C2x is finalized, people will be using C compilers that don't conform to C++20 and C2x for at least another decade, so you'll forgive me if I don't hold my breath =)
- sambe 7y agoDoesn't Nim do that?
- mhh__ 7y agoJust because you shouldn't doesn't mean you can't. (C itself is not specified very thoroughly but C - a C implementation - is, in the sense that it only does one thing for a given line of code)
- nimmer 7y agoYes, Nim uses C and GCC and this gave it very fast compile times and similar performance. It also runs on most devices supported by GCC.
- jcranmer 7y agoC is a pretty lousy intermediate language: * It's missing several useful operators, such as classic bit manipulation (count trailing zero, byteswap), or even 8- and 16-bit arithmetic. Checked arithmetic is another useful one that's not present (or even really possible in C's ABI). * Signed integer overflow is UB. * Utterly no support for SIMD types. * Proper IEEE 754 floating-point control is kind of spotty, although it tends to be as bad or worse in most other languages. * ABI control is poor. You can't come up with any way to return multiple register values, for example. * Anything that's not a vanilla function isn't supported. No uparg function support (required for Pascal), multiple entry points (required for Fortran), or zero-cost exception handling (required for C++). Hell, even computed goto isn't actually supported. And all of this is assuming you have strong control over how you expect implementation-defined behavior (e.g., sizeof(int)) to work.
- StreamBright 7y agoJust out of curiosity, what would be a great intermediate language to transpile to (as of intermediate language)?
- deleted 7y ago[deleted]
- jcranmer 7y agoOf ones I'm a familiar with, LLVM IR is probably the best, although it has other issues of its own (in particular, floating point is done even worse than C). I'm not aware of any language which is going to beat a retargeting compiler's processor-agnostic IR. But even the "better C" languages tend to not really attempt to expand C structurally. The changes amount to fixing the egregious semantics (fixed-size types, no int-promotion, define signed overflow, etc.), add vector types and other operators, maybe tweak ABI a little bit, and add a whole lot of syntactic sugar. And those languages that explore beyond C's limited structural repertoire do so at the cost of C's specificity. That said, ever since the last time someone asked me this kind of question, I've been trying to design a portable assembly language.
- bronson 7y agocfront demonstrated how this is a bad idea. And it was for C++, about as C friendly as you can get.