Intemediate code optimizer diagram

In this diagram, I have a doubt in the 2nd picture. How can different languages have same independent code optimizer ?

I think that different languages will have their own code optimizer(different) and then the optimized code will be converted to target machine code based on different machines.

So the arrow should be from one independent code optimizer (for each language) to many target machines.

Am I correct ?

  • 1
    $\begingroup$ All languages that target the JVM, such as Java, Scala, Kotlin, and Clojure, benefit from optimizations in the JVM's JIT compiler. They will, for most production-grade languages, also have higher-level optimization passes that occur before the translation to JVM bytecode, but these are independent of the target architecture so they don't multiply with the number of backends which is the point of the picture. $\endgroup$ Aug 30 '17 at 10:40
  • $\begingroup$ C and Java would have different intermediate code optimizer right ? How can they be same? Although the intermediate code generated by all these languages are same but each language would have a different way of optimizing it right ? $\endgroup$
    – Zephyr
    Aug 30 '17 at 10:41
  • 1
    $\begingroup$ C and Java don't have intermediate code at all. C and Java implementations do (or they may not). If you wrote a C compiler that used the JVM's bytecode as an intermediate language it would also benefit from optimizations done by the JVM's JIT compiler. In practice, the popular implementations of C and Java don't share an intermediate language, bu there is nothing stopping that and indeed there are front-ends for both C and Java that target LLVM, so LLVM can be seen as an intermediate language shared by some implementations of these languages. $\endgroup$ Aug 30 '17 at 10:52

The intermediate language is a relatively normal programming language and as such you can write an optimizer for it. Optimization passes like common subexpression elimination, constant propagation, or strength reductions are fairly independent of the programming language.

As long as the intermediate language can express all concepts from the source language reasonably well, there is little loss in just optimizing the intermediate representation. For example LLVM's intermediate language is well suited for representing C-like imperative languages. It also comes with a type system, so not much information is lost when translating from C, or Ada, or Java to LLVM.

There is of course a problem if the source language contains information that can't be encoded in the intermediate language. For example you could think about just optimizing x86 assembly instead of first translating to something like LLVMs IR. However assembly lacks many high level constructs that make it easier for the compiler to optimize things. For example it lacks a type system. If your Ada code restricted the value of a variable to be less than five, the compiler could use that information to optimize some conditionals. Such information would be lost when translating to Assembly.

Because of this mismatch it is not uncommon to first do some high level optimizations in the target language and then translate to the intermediate language to leverage all the work that went into the optimization passes that are suitable for many languages. This article about Rust's Mid-Level IR provides an example of this. But this step is not strictly necessary (For example Rust didn't do this at first), you just lose some optimization opportunities if you skip it.

In the end it's probably an engineering tradeoff. You lose a few opportunities for optimization by using an intermediate language. But you save enormous amounts of developer time by reusing existing optimization passes.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.