Skip to content

Commit f95d549

Browse files
WIP interference graph
WIP interference graph WIP interference graph Added some explanatory comments Add another test case for interference graph Add another test case for interference graph One for test for interference graph
1 parent c3853df commit f95d549

File tree

9 files changed

+521
-25
lines changed

9 files changed

+521
-25
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,18 @@ public class BasicBlock {
4949
* VarKill contains all the variables that are defined
5050
* in the block.
5151
*/
52-
BitSet varKill;
52+
LiveSet varKill;
5353
/**
5454
* UEVar contains upward-exposed variables in the block,
5555
* i.e. those variables that are used in the block prior to
5656
* any redefinition in the block.
5757
*/
58-
BitSet UEVar;
58+
LiveSet UEVar;
5959
/**
6060
* LiveOut is the union of variables that are live at the
6161
* head of some block that is a successor of this block.
6262
*/
63-
BitSet liveOut;
63+
LiveSet liveOut;
6464
// -----------------------
6565

6666
public BasicBlock(int bid, boolean loopHead) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,4 +560,9 @@ public StringBuilder toStr(StringBuilder sb, boolean verbose) {
560560
BasicBlock.toStr(sb, entry, new BitSet(), verbose);
561561
return sb;
562562
}
563+
564+
public void livenessAnalysis() {
565+
new Liveness(this);
566+
this.hasLiveness = true;
567+
}
563568
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ public class ExitSSA {
1717
public ExitSSA(CompiledFunction function) {
1818
this.function = function;
1919
if (!function.isSSA) throw new IllegalStateException();
20-
if (!function.hasLiveness) {
21-
new Liveness().computeLiveness(function);
22-
}
20+
function.livenessAnalysis();
2321
tree = new DominatorTree(function.entry);
2422
initStack();
2523
insertCopies(function.entry);
@@ -115,6 +113,13 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
115113
final CopyItem copyItem = workList.remove(0);
116114
final Register src = copyItem.src;
117115
final Register dest = copyItem.dest;
116+
/* Engineering a Compiler: We can avoid the lost copy
117+
problem by checking the liveness of the target name
118+
for each copy that we try to insert. When we discover
119+
a copy target that is live, we must preserve the live
120+
value in a temporary name and rewrite subsequent uses to
121+
refer to the temporary name.
122+
*/
118123
if (block.liveOut.get(dest.id)) {
119124
/* Insert a copy from dest to a new temp t at phi node defining dest */
120125
final Register t = addMoveToTempAfterPhi(block, dest);
@@ -125,11 +130,19 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
125130
addMoveAtBBEnd(block, map.get(src.id), dest);
126131
map.put(src.id, dest);
127132
/* If src is the name of a dest in copySet add item to worklist */
133+
/* see comment on phi cycles below. */
128134
CopyItem item = isCycle(copySet, src);
129135
if (item != null) {
130136
workList.add(item);
131137
}
132138
}
139+
/* Engineering a Compiler: To solve the swap problem
140+
we can detect cases where phi functions reference the
141+
targets of other phi functions in the same block. For each
142+
cycle of references, it must insert a copy to a temporary
143+
that breaks the cycle. Then we can schedule the copies to
144+
respect the dependencies implied by the phi functions.
145+
*/
133146
if (!copySet.isEmpty()) {
134147
CopyItem copyItem = copySet.remove(0);
135148
/* Insert a copy from dst to new temp at the end of Block */
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.compilerprogramming.ezlang.compiler;
2+
3+
import java.util.*;
4+
5+
public class InterferenceGraph {
6+
Map<Integer, Set<Integer>> edges = new HashMap<>();
7+
8+
private Set<Integer> addNode(Integer node) {
9+
var set = edges.get(node);
10+
if (set == null) {
11+
set = new HashSet<>();
12+
edges.put(node, set);
13+
}
14+
return set;
15+
}
16+
17+
public void addEdge(Integer from, Integer to) {
18+
if (from == to) {
19+
return;
20+
}
21+
var set1 = addNode(from);
22+
var set2 = addNode(to);
23+
set1.add(to);
24+
set2.add(from);
25+
}
26+
27+
public boolean containsEdge(Integer from, Integer to) {
28+
var set = edges.get(from);
29+
return set != null && set.contains(to);
30+
}
31+
32+
public Set<Integer> adjacents(Integer node) {
33+
return edges.get(node);
34+
}
35+
36+
public static final class Edge {
37+
public final int from;
38+
public final int to;
39+
public Edge(int from, int to) {
40+
this.from = from;
41+
this.to = to;
42+
}
43+
44+
@Override
45+
public boolean equals(Object o) {
46+
if (this == o) return true;
47+
if (o == null || getClass() != o.getClass()) return false;
48+
Edge edge = (Edge) o;
49+
return (from == edge.from && to == edge.to)
50+
|| (from == edge.to && to == edge.from);
51+
}
52+
53+
@Override
54+
public int hashCode() {
55+
return from+to;
56+
}
57+
}
58+
59+
public Set<Edge> getEdges() {
60+
Set<Edge> all = new HashSet<>();
61+
for (Integer from: edges.keySet()) {
62+
var set = edges.get(from);
63+
for (Integer to: set) {
64+
all.add(new Edge(from, to));
65+
}
66+
}
67+
return all;
68+
}
69+
70+
public String generateDotOutput() {
71+
StringBuilder sb = new StringBuilder();
72+
sb.append("digraph IGraph {\n");
73+
for (var edge: getEdges()) {
74+
sb.append(edge.from).append("->").append(edge.to).append(";\n");
75+
}
76+
sb.append("}\n");
77+
return sb.toString();
78+
}
79+
80+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.compilerprogramming.ezlang.compiler;
2+
3+
public class InterferenceGraphBuilder {
4+
5+
public InterferenceGraph build(CompiledFunction function) {
6+
InterferenceGraph graph = new InterferenceGraph();
7+
// Calculate liveOut for all basic blocks
8+
function.livenessAnalysis();
9+
System.out.println(function.toStr(new StringBuilder(), true));
10+
var blocks = BBHelper.findAllBlocks(function.entry);
11+
for (var b : blocks) {
12+
// Start with the set of live vars at the end of the block
13+
// This liveness will be updated as we look through the
14+
// instructions in the block
15+
var liveNow = b.liveOut.dup();
16+
// liveNow is initially the set of values that are live (and avail?) at the
17+
// end of the block.
18+
// Process each instruction in the block in reverse order
19+
for (var i: b.instructions.reversed()) {
20+
if (i instanceof Instruction.Move ||
21+
i instanceof Instruction.Phi) {
22+
// Move(copy) instructions are handled specially to avoid
23+
// adding an undesirable interference between the source and
24+
// destination (section 2.2.2 in Briggs thesis)
25+
// Engineering a Compiler: The copy operation does not
26+
// create an interference cause both values can occupy the
27+
// same register
28+
// Same argument applies to phi.
29+
liveNow.remove(i.uses());
30+
}
31+
if (i.definesVar()) {
32+
var def = i.def();
33+
// Defined vars interfere with all members of the live set
34+
addInterference(graph, def, liveNow);
35+
// Defined vars are removed from the live set
36+
liveNow.dead(def);
37+
}
38+
// All used vars are added to the live set
39+
liveNow.live(i.uses());
40+
}
41+
}
42+
return graph;
43+
}
44+
45+
private static void addInterference(InterferenceGraph graph, Register def, LiveSet liveSet) {
46+
for (int regNum = liveSet.nextSetBit(0); regNum >= 0; regNum = liveSet.nextSetBit(regNum+1)) {
47+
if (regNum != def.id)
48+
graph.addEdge(regNum, def.id);
49+
}
50+
}
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.compilerprogramming.ezlang.compiler;
2+
3+
import java.util.BitSet;
4+
import java.util.List;
5+
6+
public class LiveSet extends BitSet {
7+
public LiveSet(int numRegs) {
8+
super(numRegs);
9+
}
10+
public LiveSet dup() {
11+
return (LiveSet) clone();
12+
}
13+
public void live(Register r) {
14+
set(r.id, true);
15+
}
16+
public void dead(Register r) {
17+
set(r.id, false);
18+
}
19+
public void live(List<Register> regs) {
20+
for (Register r : regs) {
21+
live(r);
22+
}
23+
}
24+
public void add(Register r) {
25+
set(r.id, true);
26+
}
27+
public void remove(Register r) {
28+
set(r.id, false);
29+
}
30+
public void remove(List<Register> regs) {
31+
for (Register r : regs) {
32+
remove(r);
33+
}
34+
}
35+
public boolean isMember(Register r) {
36+
return get(r.id);
37+
}
38+
public LiveSet intersect(LiveSet other) {
39+
and(other);
40+
return this;
41+
}
42+
public LiveSet union(LiveSet other) {
43+
or(other);
44+
return this;
45+
}
46+
public LiveSet intersectNot(LiveSet other) {
47+
andNot(other);
48+
return this;
49+
}
50+
}

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111
public class Liveness {
1212

13-
public void computeLiveness(CompiledFunction function) {
13+
public Liveness(CompiledFunction function) {
1414
List<BasicBlock> blocks = BBHelper.findAllBlocks(function.entry);
1515
RegisterPool regPool = function.registerPool;
1616
init(regPool, blocks);
@@ -21,19 +21,17 @@ public void computeLiveness(CompiledFunction function) {
2121
private void init(RegisterPool regPool, List<BasicBlock> blocks) {
2222
int numRegisters = regPool.numRegisters();
2323
for (BasicBlock block : blocks) {
24-
block.UEVar = new BitSet(numRegisters);
25-
block.varKill = new BitSet(numRegisters);
26-
block.liveOut = new BitSet(numRegisters);
24+
block.UEVar = new LiveSet(numRegisters);
25+
block.varKill = new LiveSet(numRegisters);
26+
block.liveOut = new LiveSet(numRegisters);
2727
for (Instruction instruction : block.instructions) {
28-
if (instruction.usesVars()) {
29-
for (Register use : instruction.uses()) {
30-
if (!block.varKill.get(use.id))
31-
block.UEVar.set(use.id);
32-
}
28+
for (Register use : instruction.uses()) {
29+
if (!block.varKill.isMember(use))
30+
block.UEVar.add(use);
3331
}
3432
if (instruction.definesVar()) {
3533
Register def = instruction.def();
36-
block.varKill.set(def.id);
34+
block.varKill.add(def);
3735
}
3836
}
3937
}
@@ -51,15 +49,15 @@ private void computeLiveness(List<BasicBlock> blocks) {
5149
}
5250

5351
private boolean recomputeLiveOut(BasicBlock block) {
54-
BitSet oldLiveOut = (BitSet) block.liveOut.clone();
52+
LiveSet oldLiveOut = block.liveOut.dup();
5553
for (BasicBlock m: block.successors) {
56-
BitSet mLiveIn = (BitSet) m.liveOut.clone();
54+
LiveSet mLiveIn = m.liveOut.dup();
5755
// LiveOut(m) intersect not VarKill(m)
58-
mLiveIn.andNot(m.varKill);
56+
mLiveIn.intersectNot(m.varKill);
5957
// UEVar(m) union (LiveOut(m) intersect not VarKill(m))
60-
mLiveIn.or(m.UEVar);
58+
mLiveIn.union(m.UEVar);
6159
// LiveOut(block) =union (UEVar(m) union (LiveOut(m) intersect not VarKill(m)))
62-
block.liveOut.or(mLiveIn);
60+
block.liveOut.union(mLiveIn);
6361
}
6462
return !oldLiveOut.equals(block.liveOut);
6563
}

0 commit comments

Comments
 (0)