Skip to content

LLVM vs. QBE

QBE and LLVM are both compiler backends that utilize a Static Single-Assignment (SSA) form for intermediate representation. However, they are designed with fundamentally different philosophies, target audiences, and technical trade-offs. LLVM is an industry-standard, feature-rich infrastructure, whereas QBE is a minimalist and hackable backend aimed at simplicity and accessibility.

Here is a detailed comparison of the two:

1. Scope, Philosophy, and Target Audience

Factor QBE LLVM
Philosophy Aims to provide 70% of the performance of advanced compilers in 10% of the code. It prioritizes simplicity, hackability, and fast compilation. A comprehensive, modular, and reusable set of compiler and toolchain technologies designed for high performance and adaptability across various languages and platforms.
Target Audience Primarily aimed at amateur and hobbyist language designers, researchers, and those who want a simple, understandable backend to experiment with. Used in the development of industry-grade compilers (like Clang for C/C++ and Rustc for Rust), Just-In-Time (JIT) compilers, and other language-based tools.
Project Scale Intentionally small, with a codebase of around 14,000 lines of C99 and no external dependencies. A massive, large-scale project with a vast ecosystem of tools, libraries, and subprojects. It requires a modern C++ toolchain to build.

2. Intermediate Representation (IR)

The design of the Intermediate Representation (IR) is a core differentiator between the two projects.

Factor QBE LLVM
Complexity & Verbosity Features a simple, uniform IL that is less cluttered. To simplify for the user, it allows non-SSA temporaries in its input, which it converts internally. For example, incrementing a variable can be a single instruction (%v =w add %v, 1). Has a more complex, low-level, and strictly-typed IR. To ease SSA construction for frontend developers, it encourages the use of stack slots (alloca), which can lead to more verbose IR (a load, add, and store for a simple increment).
Type System Uses a much lighter type system, which results in a more readable and shorter IL with fewer casts. Has a complex and strict type system. This is crucial for advanced optimizations but often requires frontend developers to insert numerous explicit casts, which can clutter the IR.
Readability The simpler, less verbose nature of QBE’s IR makes it easier for developers to read and debug the code generated by their frontend. While human-readable, LLVM IR can be very verbose due to its strict SSA form and explicit memory operations, making it more challenging to inspect manually.

3. C ABI Compatibility

Application Binary Interface (ABI) compatibility is critical for interoperability with C libraries, and the two backends handle this responsibility differently.

Factor QBE LLVM
Implementation Provides full C ABI compatibility as a core feature. It offers IL operations to call C functions (and be called by them) without the frontend needing to implement complex ABI logic. Does not provide C ABI compatibility out-of-the-box. The responsibility for “lowering” function calls to match the target ABI falls on the language frontend.
Developer Burden Significantly reduces the burden on the language designer, as implementing a complete C ABI (especially for struct arguments and returns) is notoriously difficult and error-prone. This is a well-known issue in the LLVM community, leading to a significant amount of duplicated effort and bugs as each frontend (like Rust, Swift, etc.) must reimplement this logic.

4. Ease of Use and Learning Curve

Factor QBE LLVM
Onboarding Described as “riding a bicycle.” Its small codebase and simple C99 implementation make it highly “hackable” and easy to understand and modify. The tool takes a text file as input and outputs a text-based assembly file, which is straightforward to integrate into a build process. Often compared to “hauling your backpack with a truck.” The learning curve is steep due to its vast API, C++ implementation, and the sheer scale of the project. Writing a backend for a new target is considered a significant challenge.
Documentation & Community The project has clear documentation for its IL, but the community is much smaller. Has extensive documentation, tutorials (like the Kaleidoscope language tutorial), and a large, active community. However, finding answers to specific, complex questions can still be difficult.
Build Process Compiles in seconds without dependencies. Building LLVM is a lengthy process that can consume significant system resources. Linking against LLVM libraries can also result in slow build times for your own compiler.

5. Performance and Optimizations

Factor QBE LLVM
Optimization Goals Focuses on implementing optimizations that yield the most significant performance gains for the least amount of code—the “first 70%.” This includes passes like copy propagation, sparse conditional constant propagation, and dead code elimination. Aims for state-of-the-art performance, implementing a vast array of advanced and aggressive optimization passes that target the “last 30%” of performance.
Real-World Performance While the goal is 70% of LLVM’s performance, some user benchmarks have shown it to be closer to 50% for certain CPU-bound tasks. Performance is good enough for many uses but not intended to compete at the highest level. Generally considered the gold standard for performance in open-source compilers, producing highly optimized machine code for a wide variety of architectures.

6. Supported Architectures and Output

Factor QBE LLVM
Target Architectures Supports a limited but practical set of modern 64-bit architectures, including amd64 (Linux, macOS), arm64, and riscv64. Notably, it lacks support for 32-bit targets. Supports a very wide range of architectures, from common ones like x86 and ARM to more specialized ones like PowerPC and WebAssembly.
Output Format Emits human-readable, text-based assembly files and relies on a system-installed assembler (like as or nasm) to produce object files. Has backends that directly produce object files, bypassing the need for an external assembler.

Summary Table

Feature QBE LLVM
Primary Goal Simplicity, hackability, ease of use High performance, modularity, industry standard
Target User Hobbyist, student, researcher Professional compiler developers, large projects
C ABI Support Built-in Frontend’s responsibility
IR Style Simple, less verbose, flexible SSA Complex, strict SSA, verbose memory ops
Learning Curve Low Very High
Project Size ~14 kloc C99, no dependencies Massive C++ project with many submodules
Performance Good (“the first 70%”) Excellent (state-of-the-art)
Architecture Support Limited (amd64, arm64, riscv64) Extensive
Output Text-based assembly Object files

In conclusion, the choice between QBE and LLVM depends entirely on the project’s goals. If the aim is to learn about compilers, rapidly prototype a new language, or create a tool where ultimate performance is secondary to simplicity and maintainability, QBE is an excellent choice. For creating a production-ready, high-performance language that needs to be competitive with established languages like C++ or Rust, the power, extensive feature set, and mature ecosystem of LLVM are indispensable.

Page last modified: 2025-10-04 11:51:12