|
1 | 1 | package com.compilerprogramming.ezlang.compiler; |
2 | 2 |
|
3 | | -import java.util.ArrayList; |
4 | | -import java.util.List; |
| 3 | +import java.util.*; |
| 4 | +import java.util.stream.IntStream; |
5 | 5 |
|
| 6 | +/** |
| 7 | + * Implement the original graph coloring algorithm described by Chaitin. |
| 8 | + * |
| 9 | + * TODO spilling |
| 10 | + */ |
6 | 11 | public class ChaitinGraphColoringRegisterAllocator { |
7 | 12 |
|
8 | | - public ChaitinGraphColoringRegisterAllocator(CompiledFunction function) { |
9 | | - coalesce(function); |
| 13 | + public ChaitinGraphColoringRegisterAllocator() { |
10 | 14 | } |
11 | 15 |
|
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) { |
13 | 31 | boolean changed = true; |
| 32 | + InterferenceGraph igraph = null; |
14 | 33 | 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); |
17 | 36 | } |
| 37 | + return igraph; |
18 | 38 | } |
19 | 39 |
|
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) { |
21 | 44 | boolean changed = false; |
22 | 45 | 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) { |
28 | 51 | Register source = move.def(); |
29 | | - Register target = targetOperand.reg; |
| 52 | + Register target = registerTarget.reg; |
30 | 53 | if (source.id != target.id && |
31 | 54 | !igraph.interfere(target.id, source.id)) { |
32 | 55 | 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(); |
35 | 58 | changed = true; |
36 | 59 | } |
37 | 60 | } |
38 | 61 | } |
39 | | - for (var j: instructionsToRemove) { |
40 | | - block.instructions.set(j, new Instruction.NoOp()); |
41 | | - } |
42 | 62 | } |
43 | 63 | return changed; |
44 | 64 | } |
45 | 65 |
|
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) { |
47 | 70 | for (var block: function.getBlocks()) { |
48 | 71 | for (Instruction i: block.instructions) { |
49 | | - if (i == notNeeded) |
| 72 | + if (i == deadInstruction) |
50 | 73 | continue; |
51 | 74 | if (i.definesVar() && source.id == i.def().id) |
52 | 75 | i.replaceDef(target); |
53 | 76 | i.replaceUse(source, target); |
54 | 77 | } |
55 | 78 | } |
56 | 79 | } |
| 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 | + |
57 | 156 | } |
0 commit comments