Skip to content

Commit a152d07

Browse files
Revise coalesce to delete instruction immediately
Implemented graph coloring without any support for spills Renaming to make the implementation more clear Renaming to make the implementation more clear Update README
1 parent da25d7d commit a152d07

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)