← New search

Other meanings of Register allocation

Compiler Optimization

Register allocation

Register allocation is a compiler optimization that assigns variables to CPU registers, aiming to minimize costly memory accesses. It is a critical phase in code generation, directly impacting execution speed and code size. The problem is NP-complete in general, so compilers use heuristic algorithms such as graph coloring, linear scan, and linear programming. Register allocation also interacts with instruction scheduling and spilling, where variables are moved to memory when registers are insufficient.

NP-complete
Complexity
General register allocation is NP-complete
1970s
Origin
Graph coloring approach introduced by Chaitin
O(n)
Linear scan
Linear scan algorithm runs in linear time
~20
Typical registers
Modern CPUs have about 20–30 architectural registers
1

Problem formulation and complexity

The register allocation problem is to assign each variable (or temporary) in a program to a register, subject to the constraint that variables live simultaneously cannot share a register. This is equivalent to graph coloring, where the interference graph has a vertex per variable and an edge between variables that are live at the same time. The goal is to color the graph with k colors (k registers). Since graph coloring is NP-complete, optimal allocation is intractable for large programs, so compilers rely on heuristics.1 The problem becomes even harder with aliasing, register pairs, and irregular register architectures.

2

Algorithms and heuristics

Graph coloring, introduced by Gregory Chaitin in the 1980s, is the classic approach: it builds the interference graph, then colors it using a greedy algorithm with a spill heuristic when no color is available.2 Linear scan, proposed by Poletto and Sarkar in 1999, is a simpler and faster alternative that processes live intervals in order, making it popular in just-in-time compilers like Java HotSpot and V8.3 More recent methods use integer linear programming for optimal allocation in specific contexts, and machine learning has been explored to predict spill decisions.

3

Spilling and rematerialization

When registers are insufficient, the compiler must spill some variables to memory, inserting load and store instructions. The choice of what to spill is critical; heuristics often spill variables with high spill cost, which is the estimated cost of memory operations weighted by loop frequency. Rematerialization is a technique to recompute a value instead of loading it from memory, which is cheaper for constants or simple expressions. Modern compilers like LLVM use a spill cost model that considers the number of uses and the nesting depth of loops.

4

Lesser-known aspects

Beyond the classic algorithms, there are niche aspects: register allocation for irregular architectures, such as x86's partial registers and the 8-bit registers of 8-bit CPUs, requires special handling. The problem of coalescing moves, where copy instructions are eliminated by assigning the same register to the source and destination, is an important subproblem. Also, the concept of "register allocation by puzzle solving" was proposed in 2005, modeling the problem as a puzzle game. In embedded systems, register allocation can be done at compile time for software pipelining, and in dynamic compilation, the allocation must be fast, leading to the use of linear scan in JITs. The history includes early work by John Cocke and the first use of graph coloring in the PL.8 compiler.4

Glossary

Interference graph
A graph where vertices are variables and edges connect variables that are live simultaneously, used to model register allocation.
Spilling
The process of moving a variable from a register to memory to free up a register.
Live interval
The range of instructions during which a variable is live and must be in a register or memory.
Coalescing
The elimination of copy instructions by assigning the same register to the source and destination.
Rematerialization
Recomputing a value instead of loading it from memory, often cheaper for constants.

This article focuses on the compiler optimization sense of register allocation.