Hello! In our previous lesson, we established the "what" and "how" of LLVM's architecture, mapping the classic three-phase compiler design to specific tools like clang, opt, and llc. We saw that LLVM IR is the crucial link connecting these stages.
In this lesson, we will explore the "why." We'll delve into the specific design choices that make LLVM's architecture so powerful and versatile, going far beyond what a traditional, monolithic compiler can do. Understanding these benefits is key to appreciating why LLVM has become a foundational technology for so many tools and languages, and it will give you a deeper context for the bugs and features you'll eventually work on. We will focus on why LLVM's design as a collection of libraries, centered on a well-defined Intermediate Representation, is its greatest strength.
1. More Than a Compiler: LLVM as a Library Collection
The previous lesson might have left you with the impression that LLVM is simply a replacement for a compiler like GCC. However, one of the most profound and revolutionary aspects of LLVM's design is that it's not a single, monolithic program. Instead, it's a collection of modular and reusable libraries.
This is a critical distinction. A monolithic compiler is an application that takes source code and produces an executable. LLVM provides the components to build that application, and many others.
Let's turn again to Chris Lattner's chapter in "The Architecture of Open Source Applications" to understand this fundamental design point.
The Architecture of Open Source Applications (Volume 1)LLVM
This section explains two core tenets of LLVM's design: its complete Intermediate Representation and its structure as a set of libraries.
Please read the section "LLVM's Implementation of Three-Phase Design". Pay close attention to the contrast drawn with GCC. Focus first on the subsection on the IR, and then on the subsection about its library-based design.
As the text explains, this library-based design is what allows LLVM to be an "infrastructure." The frontend, optimizer, and backend aren't just internal stages of a single program; they are libraries with public APIs that you can use independently.
What does this enable?
- Just-In-Time (JIT) Compilers: A programming language runtime can use LLVM's libraries to compile code on the fly. When a function is called, the runtime can feed LLVM IR for that function to the LLVM optimizer and code generator libraries to get fast, native machine code. This is used by languages like Julia and in high-performance data systems.
- Static Analysis: Because Clang is a library, tools like
clang-tidyandclang-formatcan parse C++ code into an Abstract Syntax Tree (AST), analyze it for bugs or style violations, and even suggest automatic fixes, all without running a full compilation. - Custom Tools: You can build your own specialized tools. For example, a video game engine might create a tool that parses a custom shader language, runs a few specific LLVM optimization passes, and generates code for a GPU.
For you as a future contributor, this is the most important concept to grasp. Much of your work will not involve changing clang to compile C++ differently, but rather using the Clang and LLVM libraries to build or improve tools that operate on code.
2. The Power of a "Perfect" Intermediate Representation
The previous reading also highlighted that LLVM IR is a "complete code representation." This seemingly technical detail has enormous practical benefits that directly flow from the three-phase design.
Because the optimizer's only interface is the IR, and because this IR can be saved to disk as text (.ll files) or a compact binary format (.bc files), we can stop and start the compilation process at will. The compiler's phases don't have to run all at once. This decoupling in time and space enables powerful features.

Link-Time Optimization (LTO)
Normally, a compiler optimizes one .cpp file (a translation unit) at a time. It can't see the code in other files, so it can't perform optimizations like inlining a function from b.cpp into a.cpp.
LTO solves this. With LTO, the frontend doesn't emit native object code, but instead writes the LLVM bitcode into the .o file. At the final link step, the linker gathers all this bitcode, merges it into one large module, and then runs the LLVM optimizer over the entire program. This gives the optimizer global visibility, enabling a whole new class of powerful inter-procedural optimizations.
Let's see how the AOSA chapter describes this process.
The Architecture of Open Source Applications (Volume 1)LLVM
This section describes some of the powerful capabilities that result from LLVM's modular, library-based architecture.
Please read the subsection on LTO and ITO. The diagrams in this section are particularly helpful for visualizing the flow of data.
Robust Testing
Another huge benefit, especially for contributors, is in testing. Since you can represent code directly in IR's textual format, you can write a very small .ll test case that precisely triggers a bug in an optimization pass. You don't need to find a complex C++ program that happens to generate the problematic IR sequence. This makes bug reports smaller, more precise, and makes writing regression tests for your fixes much simpler. We will work extensively with this workflow in Module 3.
3. A Generic Optimizer That's Anything But
A common question is: if the optimizer is language- and target-independent, how can it make good decisions for a specific CPU? For instance, an instruction might be cheap on one processor but extremely expensive on another. A truly generic optimizer might make a poor choice.
LLVM's architecture has an elegant solution for this: the TargetTransformInfo (TTI). This is an interface that allows the middle-end (the optimizer) to query the backend for target-specific information. An optimization pass can ask the TTI questions like:
- "What is the estimated cost of this sequence of IR instructions on the current target?"
- "Does this target have a hardware instruction for integer division?"
- "What is the CPU's cache line size?"
The pass can then use these answers to tune its heuristics. This allows the optimizer to remain generic while still making highly informed, target-specific decisions.
A talk from the LLVM Developers' Meeting provides an excellent, concrete example of why this is necessary.
2019 LLVM Developers’ Meeting: J. Paquette & F. Hahn “Getting Started With LLVM: Basics”
This video discusses how middle-end transformations can be made aware of target-specific costs.
Watch the segment from this timestamp. It explains the problem of target-specific costs and introduces the Target Transform Info (TTI) as the solution. The example of the UI2FP instruction's cost varying on different ARM64 sub-targets is particularly insightful.
This TTI mechanism is a perfect example of the careful design that separates LLVM's components. The optimizer doesn't need to know the details of every target; it just needs to know how to ask the right questions through a stable interface. This keeps the middle-end clean while allowing the backend to provide the specialized knowledge needed for high-performance code generation.
Conclusion
In this lesson, we've explored the core benefits that arise from LLVM's three-phase design. It's not just about separating concerns, but about how that separation is implemented.
Key Takeaways:
- LLVM is a collection of libraries, not a monolithic application. This is its most powerful feature, enabling a vast ecosystem of tools for JIT compilation, static analysis, and more.
- LLVM IR is a complete, self-contained representation. Because it can be serialized and deserialized losslessly, it allows compilation phases to be decoupled, enabling powerful features like Link-Time Optimization (LTO).
- The textual form of IR is crucial for testing. It allows contributors to create minimal, precise test cases for optimizer bugs.
- The Target Transform Info (TTI) interface allows the generic optimizer to make target-aware decisions. It queries the backend for cost models and hardware features, bridging the gap between the middle-end and backend.
These design principles are what make LLVM so successful and are fundamental to understanding how to contribute to it.
In our next lesson, we'll zoom out slightly and get a "lay of the land" by identifying the major subprojects like Clang, LLDB, and MLIR that live within the main LLVM project, preparing you to navigate the vast codebase.