Compiler Types and Specialized Techniques
Understand source‑to‑source, bytecode, and JIT compilers, their distinction from assemblers, and how JIT operates within an interpreter.
Summary
Read Summary
Flashcards
Save Flashcards
Quiz
Take Quiz
Quick Practice
What is the primary function of a source-to-source compiler?
1 of 7
Summary
Types of Compilers
Introduction
Compilers are not all the same. Rather than always converting source code directly to machine code for a specific processor, modern compilers take various approaches depending on their design goals. Understanding these different types helps explain why languages like Java and Python run on many different machines, and why JavaScript in your browser keeps getting faster while you use it.
Source-to-Source Compilers
A source-to-source compiler (also called a transcompiler or transpiler) takes source code written in one high-level programming language and outputs source code in a different high-level programming language. Rather than producing machine code, it produces code you could theoretically read and modify.
A common modern example is when you write code in a newer version of JavaScript (like ES6) but compile it to an older version of JavaScript (ES5) so it runs in older browsers. The input is a high-level language, and the output is also a high-level language—just a different one. This is useful when you want to use modern language features but need compatibility with older systems.
Bytecode Compilers
A bytecode compiler translates source code into the assembly language of a theoretical machine rather than a real processor. This intermediate representation is called bytecode. The bytecode is not machine code for your actual computer—it's code for an imaginary, standardized machine.
Why is this useful? Consider that you want a program to run on Windows, macOS, and Linux without rewriting it three times. If you compile to bytecode:
The compiler produces the same bytecode regardless of the operating system
Each operating system has an interpreter that understands how to run this bytecode on that specific platform
You achieve platform independence—the same compiled code runs everywhere
Java and Python both use bytecode compilers. Java produces bytecode for the Java Virtual Machine (JVM), and Python produces bytecode that the Python interpreter executes. The class files you see from Java compilation contain bytecode, not native machine code.
Just-In-Time Compilers
A Just-In-Time (JIT) compiler is fundamentally different from traditional compilers because it postpones compilation until the program is actually running. While a traditional compiler translates everything before execution starts, a JIT compiler watches the running program and makes compilation decisions on the fly.
The key innovation is that JIT compilers identify "hot" code paths—sections of code that execute frequently. When the JIT compiler detects hot code, it compiles that specific section from bytecode (or source) into optimized machine code, improving performance significantly. This means your program starts running quickly (interpreted bytecode is fast to start), but frequently-executed sections become as fast as natively compiled code.
This creates an important tradeoff:
Traditional compilation: Everything is compiled before running. Startup is slow, but everything runs at native speed.
JIT compilation: Program starts instantly with interpretation, but hot code gets optimized during execution. After "warming up," it reaches peak performance.
Languages and platforms using JIT compilation include Python, JavaScript, Java, Smalltalk, and Microsoft's Common Language Infrastructure (used by .NET languages).
The Interpreter and JIT Relationship
Understanding JIT compilation requires knowing how it fits into the broader execution model. A JIT compiler typically doesn't run standalone—it operates inside an interpreter. Here's the workflow:
The interpreter begins executing bytecode instruction-by-instruction
As the program runs, the JIT compiler monitors which code sections execute repeatedly
When a code path becomes "hot" (exceeds a frequency threshold), the JIT compiler compiles that bytecode section into optimized machine code
Future executions of that hot section run the optimized machine code instead of going through the interpreter
This is why JavaScript engines like V8 (used in Chrome) can achieve impressive performance: they start execution immediately with interpretation, but by the time you're in a tight loop doing calculations, that loop is running as compiled machine code.
Assemblers Are Not Compilers
An important distinction: an assembler is not a compiler. Assemblers translate human-readable assembly language directly into machine code. While this sounds similar to compilation, assemblers work at a much lower level of abstraction. Assembly language has essentially a one-to-one mapping with machine instructions—each assembly instruction typically becomes one machine instruction. Compilers, by contrast, handle high-level language constructs (like loops, function calls, and object-oriented features) that require substantial translation and optimization.
Flashcards
What is the primary function of a source-to-source compiler?
It takes a program written in one high-level language and outputs a program in another high-level language.
What are two alternative names for source-to-source compilers?
Transcompilers or transpilers.
Into what kind of target do bytecode compilers translate source code?
The assembly language of a theoretical machine.
What is the purpose of using bytecode compilers in languages like Java and Python?
To produce platform-independent intermediate representations.
When does a Just-In-Time (JIT) compiler perform its compilation?
It postpones compilation until the program is running.
What specific sections of code does a Just-In-Time compiler target for compilation to improve performance?
“Hot” code paths (sections executed frequently).
In a typical execution environment, how does a Just-In-Time compiler interact with an interpreter?
The interpreter executes bytecode first and then invokes the JIT compiler for hot code paths.
Quiz
Compiler Types and Specialized Techniques Quiz Question 1: What is the primary behavior of a Just‑In‑Time (JIT) compiler regarding when it performs compilation?
- It postpones compilation until the program is running. (correct)
- It compiles the entire program before execution begins.
- It translates machine code back into source code.
- It only assembles human‑readable assembly into machine code.
Compiler Types and Specialized Techniques Quiz Question 2: Into what form does a bytecode compiler translate source code?
- The assembly language of a theoretical machine (correct)
- Direct native machine code
- High‑level source code in another language
- Human‑readable pseudo‑code
Compiler Types and Specialized Techniques Quiz Question 3: Which languages are mentioned as using bytecode compilers to create platform‑independent intermediate representations?
- Java and Python (correct)
- C and C++
- JavaScript and Ruby
- Fortran and COBOL
Compiler Types and Specialized Techniques Quiz Question 4: Inside what type of runtime component does a Just‑In‑Time compiler typically operate?
- An interpreter (correct)
- A linker
- A static analyzer
- A garbage collector
Compiler Types and Specialized Techniques Quiz Question 5: What does an assembler do that distinguishes it from a typical compiler?
- It translates assembly language into machine code (correct)
- It converts high‑level code directly into bytecode
- It optimizes source code before compilation
- It generates source code in another high‑level language
Compiler Types and Specialized Techniques Quiz Question 6: The names “transcompiler” and “transpiler” refer to which type of compiler?
- Source‑to‑source compiler (correct)
- Bytecode compiler
- Just‑In‑Time (JIT) compiler
- Assembler
What is the primary behavior of a Just‑In‑Time (JIT) compiler regarding when it performs compilation?
1 of 6
Key Concepts
Compilers and Transpilers
Source‑to‑source compiler
Transcompiler (or transpiler)
Bytecode compiler
Just‑in‑time (JIT) compiler
Hot code path
Intermediate representation (IR)
Execution and Translation
Interpreter
Assembler
Warren Abstract Machine (WAM)
Common Intermediate Language (CIL)
Definitions
Source‑to‑source compiler
A compiler that translates code written in one high‑level language into another high‑level language.
Transcompiler (or transpiler)
A synonym for source‑to‑source compiler, often used for converting between programming languages.
Bytecode compiler
A compiler that converts source code into an intermediate, platform‑independent bytecode representation.
Just‑in‑time (JIT) compiler
A compiler that generates native machine code at runtime for frequently executed (“hot”) code paths.
Hot code path
A segment of a program that is executed repeatedly enough to trigger JIT compilation for performance gains.
Warren Abstract Machine (WAM)
A theoretical machine designed to execute Prolog bytecode, often targeted by Prolog bytecode compilers.
Intermediate representation (IR)
A language‑agnostic code form used by compilers (e.g., bytecode) to enable optimization and portability.
Interpreter
A program that directly executes source code or bytecode without prior full compilation to machine code.
Assembler
A tool that translates human‑readable assembly language into machine code, distinct from a compiler.
Common Intermediate Language (CIL)
The standardized bytecode used by Microsoft .NET languages, compiled just‑in‑time to native code.