Skip to content

Commit a136be2

Browse files
Merge pull request #15 from CompilerProgramming/chaitin
Chatin Graph Coloring Register Allocator continued
2 parents da25d7d + a152d07 commit a136be2

File tree

11 files changed

+241
-65
lines changed

11 files changed

+241
-65
lines changed

optvm/README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,36 @@ This module implements various compiler optimization techniques such as:
44

55
* Static Single Assignment
66
* Liveness Analysis
7-
* WIP Graph Coloring Register Allocator (Chaitin)
7+
* Graph Coloring Register Allocator (Chaitin)
8+
9+
Our goal here is to perform optimizations on the Intermediate Representation targeting an abstract machine, rather than
10+
a physical machine. Therefore, all our optimization passes will work on the instruction set of this abstract machine.
11+
12+
13+
## Guide
14+
15+
* [Register](src/main/java/com/compilerprogramming/ezlang/compiler/Register.java) - implements a virtual register. Virtual registers
16+
have a name, type, and id - the id is unique, but name is not. Initially the compiler generates unique registers for every local
17+
and temporary, the number of registers grows as we convert to SSA and back out of SSA. Finally, as we run the Chaitin register allocator
18+
we shrink down the number of virtual registers to the minimum.
19+
* [RegisterPool](src/main/java/com/compilerprogramming/ezlang/compiler/RegisterPool.java) - simple pool to allow us to find a register
20+
by its id, and to allocate new virtual registers.
21+
* [CompiledFunction](src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java) - encapsulates the IR for a single function.
22+
* [BasicBlock](src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java) - Defines our basic block - which contains instructions
23+
that execute sequentially. A basic block ends with a branch. There are two distinguished basic blocks in every function: entry and exit.
24+
* [BBHelper](src/main/java/com/compilerprogramming/ezlang/compiler/BBHelper.java) - Some utilities that manipulate basic blocks.
25+
* [Operand](src/main/java/com/compilerprogramming/ezlang/compiler/Operand.java) - Operands in instructions.
26+
* [Instruction](src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java) - Instructions in basic blocks.
27+
* [DominatorTree](src/main/java/com/compilerprogramming/ezlang/compiler/DominatorTree.java) - Calculates dominator tree and dominance frontiers.
28+
* [LiveSet](src/main/java/com/compilerprogramming/ezlang/compiler/LiveSet.java) - Bitset used to track liveness of registers.
29+
* [Liveness](src/main/java/com/compilerprogramming/ezlang/compiler/Liveness.java) - Liveness calculator, works for both SSA and non-SSA forms.
30+
* [EnterSSA](src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java) - Transforms into SSA, using algorithm by Preston Briggs.
31+
* [ExitSSA](src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java) - Exits SSA form, using algorithm by Preston Briggs.
32+
* [LoopFinder](src/main/java/com/compilerprogramming/ezlang/compiler/LoopFinder.java) - Discovers loops.
33+
* [LoopNest](src/main/java/com/compilerprogramming/ezlang/compiler/LoopNest.java) - Representation of loop nesting.
34+
* [InterferenceGraph](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Representation of an Interference Graph
35+
required by the register allocator.
36+
* [InterferenceGraphBuilder](src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java) - Constructs InteferenceGraph for a set
37+
of basic bocks, using liveness information.
38+
* [ChaitinGraphColoringRegisterAllocator](src/main/java/com/compilerprogramming/ezlang/compiler/ChaitinGraphColoringRegisterAllocator.java) - basic
39+
Chaitin Graph Coloring Register Allocator - WIP.

optvm/src/main/java/com/compilerprogramming/ezlang/compiler/BasicBlock.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ public List<Instruction.Phi> phis() {
126126
}
127127
return list;
128128
}
129+
public int whichPred(BasicBlock s) {
130+
int i = 0;
131+
for (BasicBlock p: s.predecessors) {
132+
if (p == this)
133+
return i;
134+
i++;
135+
}
136+
throw new IllegalStateException();
137+
}
129138
public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visited, boolean dumpLiveness)
130139
{
131140
if (visited.get(bb.bid))
Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,156 @@
11
package com.compilerprogramming.ezlang.compiler;
22

3-
import java.util.ArrayList;
4-
import java.util.List;
3+
import java.util.*;
4+
import java.util.stream.IntStream;
55

6+
/**
7+
* Implement the original graph coloring algorithm described by Chaitin.
8+
*
9+
* TODO spilling
10+
*/
611
public class ChaitinGraphColoringRegisterAllocator {
712

8-
public ChaitinGraphColoringRegisterAllocator(CompiledFunction function) {
9-
coalesce(function);
13+
public ChaitinGraphColoringRegisterAllocator() {
1014
}
1115

12-
private void coalesce(CompiledFunction function) {
16+
public Map<Integer, Integer> assignRegisters(CompiledFunction function, int numRegisters) {
17+
if (function.isSSA) throw new IllegalStateException("Register allocation should be done after exiting SSA");
18+
var g = coalesce(function);
19+
var registers = registersInIR(function);
20+
var colors = IntStream.range(0, numRegisters).boxed().toList();
21+
// TODO pre-assign regs to args
22+
// TODO spilling
23+
var assignments = colorGraph(g, registers, new HashSet<>(colors));
24+
return assignments;
25+
}
26+
27+
/**
28+
* Chaitin: coalesce_nodes - coalesce away copy operations
29+
*/
30+
public InterferenceGraph coalesce(CompiledFunction function) {
1331
boolean changed = true;
32+
InterferenceGraph igraph = null;
1433
while (changed) {
15-
var igraph = new InterferenceGraphBuilder().build(function);
16-
changed = coalesceRegisters(function, igraph);
34+
igraph = new InterferenceGraphBuilder().build(function);
35+
changed = coalesceCopyOperations(function, igraph);
1736
}
37+
return igraph;
1838
}
1939

20-
private boolean coalesceRegisters(CompiledFunction function, InterferenceGraph igraph) {
40+
/**
41+
* Chaitin: coalesce_nodes - coalesce away copy operations
42+
*/
43+
private boolean coalesceCopyOperations(CompiledFunction function, InterferenceGraph igraph) {
2144
boolean changed = false;
2245
for (var block: function.getBlocks()) {
23-
List<Integer> instructionsToRemove = new ArrayList<>();
24-
for (int j = 0; j < block.instructions.size(); j++) {
25-
Instruction i = block.instructions.get(j);
26-
if (i instanceof Instruction.Move move
27-
&& move.from() instanceof Operand.RegisterOperand targetOperand) {
46+
Iterator<Instruction> iter = block.instructions.iterator();
47+
while (iter.hasNext()) {
48+
Instruction instruction = iter.next();
49+
if (instruction instanceof Instruction.Move move
50+
&& move.from() instanceof Operand.RegisterOperand registerTarget) {
2851
Register source = move.def();
29-
Register target = targetOperand.reg;
52+
Register target = registerTarget.reg;
3053
if (source.id != target.id &&
3154
!igraph.interfere(target.id, source.id)) {
3255
igraph.rename(source.id, target.id);
33-
rewriteInstructions(function, i, source, target);
34-
instructionsToRemove.add(j);
56+
rewriteInstructions(function, instruction, source, target);
57+
iter.remove();
3558
changed = true;
3659
}
3760
}
3861
}
39-
for (var j: instructionsToRemove) {
40-
block.instructions.set(j, new Instruction.NoOp());
41-
}
4262
}
4363
return changed;
4464
}
4565

46-
private void rewriteInstructions(CompiledFunction function, Instruction notNeeded, Register source, Register target) {
66+
/**
67+
* Chaitin: rewrite_il
68+
*/
69+
private void rewriteInstructions(CompiledFunction function, Instruction deadInstruction, Register source, Register target) {
4770
for (var block: function.getBlocks()) {
4871
for (Instruction i: block.instructions) {
49-
if (i == notNeeded)
72+
if (i == deadInstruction)
5073
continue;
5174
if (i.definesVar() && source.id == i.def().id)
5275
i.replaceDef(target);
5376
i.replaceUse(source, target);
5477
}
5578
}
5679
}
80+
81+
/**
82+
* Get the list of registers in use in the Intermediate Code
83+
* Chaitin: registers_in_il()
84+
*/
85+
private Set<Integer> registersInIR(CompiledFunction function) {
86+
Set<Integer> registers = new HashSet<>();
87+
for (var block: function.getBlocks()) {
88+
Iterator<Instruction> iter = block.instructions.iterator();
89+
while (iter.hasNext()) {
90+
Instruction instruction = iter.next();
91+
if (instruction.definesVar())
92+
registers.add(instruction.def().id);
93+
for (Register use: instruction.uses())
94+
registers.add(use.id);
95+
}
96+
}
97+
return registers;
98+
}
99+
100+
/**
101+
* Chaitin: color_graph line 2-3
102+
*/
103+
private Integer findNodeWithNeighborCountLessThan(InterferenceGraph g, Set<Integer> nodes, int numColors) {
104+
for (var node: nodes) {
105+
if (g.neighbors(node).size() < numColors) {
106+
return node;
107+
}
108+
}
109+
return null;
110+
}
111+
112+
private Set<Integer> getNeighborColors(InterferenceGraph g, Integer node, Map<Integer,Integer> assignedColors) {
113+
Set<Integer> colors = new HashSet<>();
114+
for (var neighbour: g.neighbors(node)) {
115+
var c = assignedColors.get(neighbour);
116+
if (c != null) {
117+
colors.add(c);
118+
}
119+
}
120+
return colors;
121+
}
122+
123+
private Integer chooseSomeColorNotAssignedToNeighbors(Set<Integer> colors, Set<Integer> neighborColors) {
124+
// Create new color set that removes the colors assigned to neighbors
125+
var set = new HashSet<>(colors);
126+
set.removeAll(neighborColors);
127+
// pick a random color (we pick the first)
128+
return set.stream().findAny().orElseThrow();
129+
}
130+
131+
private static HashSet<Integer> subtract(Set<Integer> originalSet, Integer node) {
132+
var reducedSet = new HashSet<>(originalSet);
133+
reducedSet.remove(node);
134+
return reducedSet;
135+
}
136+
137+
/**
138+
* Chaitin: color_graph
139+
*/
140+
private Map<Integer, Integer> colorGraph(InterferenceGraph g, Set<Integer> nodes, Set<Integer> colors) {
141+
if (nodes.size() == 0)
142+
return new HashMap<>();
143+
var numColors = colors.size();
144+
var node = findNodeWithNeighborCountLessThan(g, nodes, numColors);
145+
if (node == null)
146+
return null;
147+
var coloring = colorGraph(g.dup().subtract(node), subtract(nodes, node), colors);
148+
if (coloring == null)
149+
return null;
150+
var neighbourColors = getNeighborColors(g, node, coloring);
151+
var color = chooseSomeColorNotAssignedToNeighbors(colors, neighbourColors);
152+
coloring.put(node, color);
153+
return coloring;
154+
}
155+
57156
}

optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SSATransform.java renamed to optvm/src/main/java/com/compilerprogramming/ezlang/compiler/EnterSSA.java

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* 'Practical Improvements to the Construction and Destruction
99
* of Single Static Assigment Form' by Preston Briggs.
1010
*/
11-
public class SSATransform {
11+
public class EnterSSA {
1212

1313
CompiledFunction function;
1414
DominatorTree domTree;
@@ -22,12 +22,12 @@ public class SSATransform {
2222
int[] counters;
2323
VersionStack[] stacks;
2424

25-
public SSATransform(CompiledFunction bytecodeFunction) {
25+
public EnterSSA(CompiledFunction bytecodeFunction) {
2626
this.function = bytecodeFunction;
2727
setupGlobals();
2828
computeDomTreeAndDominanceFrontiers();
2929
this.blocks = domTree.blocks;
30-
findGlobalVars();
30+
findNonLocalNames();
3131
insertPhis();
3232
renameVars();
3333
bytecodeFunction.isSSA = true;
@@ -47,7 +47,7 @@ private void setupGlobals() {
4747
* Compute set of registers that are live across multiple blocks
4848
* i.e. are not exclusively used in a single block.
4949
*/
50-
private void findGlobalVars() {
50+
private void findNonLocalNames() {
5151
for (BasicBlock block : blocks) {
5252
var varKill = new HashSet<Integer>();
5353
for (Instruction instruction: block.instructions) {
@@ -142,7 +142,7 @@ void search(BasicBlock block) {
142142
}
143143
// Update phis in successor blocks
144144
for (BasicBlock s: block.successors) {
145-
int j = whichPred(s,block);
145+
int j = block.whichPred(s);
146146
for (Instruction.Phi phi: s.phis()) {
147147
Register oldReg = phi.input(j);
148148
phi.replaceInput(j, stacks[oldReg.nonSSAId()].top());
@@ -161,16 +161,6 @@ void search(BasicBlock block) {
161161
}
162162
}
163163

164-
public static int whichPred(BasicBlock s, BasicBlock block) {
165-
int i = 0;
166-
for (BasicBlock p: s.predecessors) {
167-
if (p == block)
168-
return i;
169-
i++;
170-
}
171-
throw new IllegalStateException();
172-
}
173-
174164
private void initVersionCounters() {
175165
counters = new int[nonLocalNames.length];
176166
stacks = new VersionStack[nonLocalNames.length];

optvm/src/main/java/com/compilerprogramming/ezlang/compiler/ExitSSA.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
8282
Map<Integer, Register> map = new HashMap<>();
8383
BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2);
8484
for (BasicBlock s: block.successors) {
85-
int j = SSATransform.whichPred(s, block);
85+
int j = block.whichPred(s);
8686
for (Instruction.Phi phi: s.phis()) {
8787
Register dst = phi.value();
8888
Register src = phi.input(j); // jth operand of phi node

optvm/src/main/java/com/compilerprogramming/ezlang/compiler/InterferenceGraph.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.*;
44

55
public class InterferenceGraph {
6-
Map<Integer, Set<Integer>> edges = new HashMap<>();
6+
private Map<Integer, Set<Integer>> edges = new HashMap<>();
77

88
private Set<Integer> addNode(Integer node) {
99
var set = edges.get(node);
@@ -24,6 +24,32 @@ public void addEdge(Integer from, Integer to) {
2424
set2.add(from);
2525
}
2626

27+
/**
28+
* Remove a node from the interference graph
29+
* deleting it from all adjacency lists
30+
*/
31+
public InterferenceGraph subtract(Integer node) {
32+
edges.remove(node);
33+
for (var key : edges.keySet()) {
34+
var neighbours = edges.get(key);
35+
neighbours.remove(key);
36+
}
37+
return this;
38+
}
39+
40+
/**
41+
* Duplicate an interference graph
42+
*/
43+
public InterferenceGraph dup() {
44+
var igraph = new InterferenceGraph();
45+
igraph.edges = new HashMap<>();
46+
for (var key : edges.keySet()) {
47+
var neighbours = edges.get(key);
48+
igraph.edges.put(key, new HashSet<>(neighbours));
49+
}
50+
return igraph;
51+
}
52+
2753
public boolean interfere(Integer from, Integer to) {
2854
var set = edges.get(from);
2955
return set != null && set.contains(to);
@@ -50,8 +76,15 @@ public void rename(Integer source, Integer target) {
5076
}
5177
}
5278

53-
public Set<Integer> adjacents(Integer node) {
54-
return edges.get(node);
79+
/**
80+
* Get neighbours of the node
81+
* Chaitin: neighbors()
82+
*/
83+
public Set<Integer> neighbors(Integer node) {
84+
var adjacents = edges.get(node);
85+
if (adjacents == null)
86+
adjacents = Collections.emptySet();
87+
return adjacents;
5588
}
5689

5790
public static final class Edge {

optvm/src/main/java/com/compilerprogramming/ezlang/compiler/LiveSet.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public void remove(List<Register> regs) {
3232
remove(r);
3333
}
3434
}
35-
public boolean isMember(Register r) {
35+
public boolean contains(Register r) {
3636
return get(r.id);
3737
}
3838
public LiveSet intersect(LiveSet other) {
@@ -43,7 +43,10 @@ public LiveSet union(LiveSet other) {
4343
or(other);
4444
return this;
4545
}
46-
public LiveSet intersectNot(LiveSet other) {
46+
/**
47+
* Computes this - other.
48+
*/
49+
public LiveSet subtract(LiveSet other) {
4750
andNot(other);
4851
return this;
4952
}

0 commit comments

Comments
 (0)