Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ public class BasicBlock {
* VarKill contains all the variables that are defined
* in the block.
*/
BitSet varKill;
LiveSet varKill;
/**
* UEVar contains upward-exposed variables in the block,
* i.e. those variables that are used in the block prior to
* any redefinition in the block.
*/
BitSet UEVar;
LiveSet UEVar;
/**
* LiveOut is the union of variables that are live at the
* head of some block that is a successor of this block.
*/
BitSet liveOut;
LiveSet liveOut;
// -----------------------

public BasicBlock(int bid, boolean loopHead) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,9 @@ public StringBuilder toStr(StringBuilder sb, boolean verbose) {
BasicBlock.toStr(sb, entry, new BitSet(), verbose);
return sb;
}

public void livenessAnalysis() {
new Liveness(this);
this.hasLiveness = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ public class ExitSSA {
public ExitSSA(CompiledFunction function) {
this.function = function;
if (!function.isSSA) throw new IllegalStateException();
if (!function.hasLiveness) {
new Liveness().computeLiveness(function);
}
function.livenessAnalysis();
tree = new DominatorTree(function.entry);
initStack();
insertCopies(function.entry);
Expand Down Expand Up @@ -115,6 +113,13 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
final CopyItem copyItem = workList.remove(0);
final Register src = copyItem.src;
final Register dest = copyItem.dest;
/* Engineering a Compiler: We can avoid the lost copy
problem by checking the liveness of the target name
for each copy that we try to insert. When we discover
a copy target that is live, we must preserve the live
value in a temporary name and rewrite subsequent uses to
refer to the temporary name.
*/
if (block.liveOut.get(dest.id)) {
/* Insert a copy from dest to a new temp t at phi node defining dest */
final Register t = addMoveToTempAfterPhi(block, dest);
Expand All @@ -125,11 +130,19 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
addMoveAtBBEnd(block, map.get(src.id), dest);
map.put(src.id, dest);
/* If src is the name of a dest in copySet add item to worklist */
/* see comment on phi cycles below. */
CopyItem item = isCycle(copySet, src);
if (item != null) {
workList.add(item);
}
}
/* Engineering a Compiler: To solve the swap problem
we can detect cases where phi functions reference the
targets of other phi functions in the same block. For each
cycle of references, it must insert a copy to a temporary
that breaks the cycle. Then we can schedule the copies to
respect the dependencies implied by the phi functions.
*/
if (!copySet.isEmpty()) {
CopyItem copyItem = copySet.remove(0);
/* Insert a copy from dst to new temp at the end of Block */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.compilerprogramming.ezlang.compiler;

import java.util.*;

public class InterferenceGraph {
Map<Integer, Set<Integer>> edges = new HashMap<>();

private Set<Integer> addNode(Integer node) {
var set = edges.get(node);
if (set == null) {
set = new HashSet<>();
edges.put(node, set);
}
return set;
}

public void addEdge(Integer from, Integer to) {
if (from == to) {
return;
}
var set1 = addNode(from);
var set2 = addNode(to);
set1.add(to);
set2.add(from);
}

public boolean containsEdge(Integer from, Integer to) {
var set = edges.get(from);
return set != null && set.contains(to);
}

public Set<Integer> adjacents(Integer node) {
return edges.get(node);
}

public static final class Edge {
public final int from;
public final int to;
public Edge(int from, int to) {
this.from = from;
this.to = to;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Edge edge = (Edge) o;
return (from == edge.from && to == edge.to)
|| (from == edge.to && to == edge.from);
}

@Override
public int hashCode() {
return from+to;
}
}

public Set<Edge> getEdges() {
Set<Edge> all = new HashSet<>();
for (Integer from: edges.keySet()) {
var set = edges.get(from);
for (Integer to: set) {
all.add(new Edge(from, to));
}
}
return all;
}

public String generateDotOutput() {
StringBuilder sb = new StringBuilder();
sb.append("digraph IGraph {\n");
for (var edge: getEdges()) {
sb.append(edge.from).append("->").append(edge.to).append(";\n");
}
sb.append("}\n");
return sb.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.compilerprogramming.ezlang.compiler;

public class InterferenceGraphBuilder {

public InterferenceGraph build(CompiledFunction function) {
InterferenceGraph graph = new InterferenceGraph();
// Calculate liveOut for all basic blocks
function.livenessAnalysis();
System.out.println(function.toStr(new StringBuilder(), true));
var blocks = BBHelper.findAllBlocks(function.entry);
for (var b : blocks) {
// Start with the set of live vars at the end of the block
// This liveness will be updated as we look through the
// instructions in the block
var liveNow = b.liveOut.dup();
// liveNow is initially the set of values that are live (and avail?) at the
// end of the block.
// Process each instruction in the block in reverse order
for (var i: b.instructions.reversed()) {
if (i instanceof Instruction.Move ||
i instanceof Instruction.Phi) {
// Move(copy) instructions are handled specially to avoid
// adding an undesirable interference between the source and
// destination (section 2.2.2 in Briggs thesis)
// Engineering a Compiler: The copy operation does not
// create an interference cause both values can occupy the
// same register
// Same argument applies to phi.
liveNow.remove(i.uses());
}
if (i.definesVar()) {
var def = i.def();
// Defined vars interfere with all members of the live set
addInterference(graph, def, liveNow);
// Defined vars are removed from the live set
liveNow.dead(def);
}
// All used vars are added to the live set
liveNow.live(i.uses());
}
}
return graph;
}

private static void addInterference(InterferenceGraph graph, Register def, LiveSet liveSet) {
for (int regNum = liveSet.nextSetBit(0); regNum >= 0; regNum = liveSet.nextSetBit(regNum+1)) {
if (regNum != def.id)
graph.addEdge(regNum, def.id);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.compilerprogramming.ezlang.compiler;

import java.util.BitSet;
import java.util.List;

public class LiveSet extends BitSet {
public LiveSet(int numRegs) {
super(numRegs);
}
public LiveSet dup() {
return (LiveSet) clone();
}
public void live(Register r) {
set(r.id, true);
}
public void dead(Register r) {
set(r.id, false);
}
public void live(List<Register> regs) {
for (Register r : regs) {
live(r);
}
}
public void add(Register r) {
set(r.id, true);
}
public void remove(Register r) {
set(r.id, false);
}
public void remove(List<Register> regs) {
for (Register r : regs) {
remove(r);
}
}
public boolean isMember(Register r) {
return get(r.id);
}
public LiveSet intersect(LiveSet other) {
and(other);
return this;
}
public LiveSet union(LiveSet other) {
or(other);
return this;
}
public LiveSet intersectNot(LiveSet other) {
andNot(other);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
public class Liveness {

public void computeLiveness(CompiledFunction function) {
public Liveness(CompiledFunction function) {
List<BasicBlock> blocks = BBHelper.findAllBlocks(function.entry);
RegisterPool regPool = function.registerPool;
init(regPool, blocks);
Expand All @@ -21,19 +21,17 @@ public void computeLiveness(CompiledFunction function) {
private void init(RegisterPool regPool, List<BasicBlock> blocks) {
int numRegisters = regPool.numRegisters();
for (BasicBlock block : blocks) {
block.UEVar = new BitSet(numRegisters);
block.varKill = new BitSet(numRegisters);
block.liveOut = new BitSet(numRegisters);
block.UEVar = new LiveSet(numRegisters);
block.varKill = new LiveSet(numRegisters);
block.liveOut = new LiveSet(numRegisters);
for (Instruction instruction : block.instructions) {
if (instruction.usesVars()) {
for (Register use : instruction.uses()) {
if (!block.varKill.get(use.id))
block.UEVar.set(use.id);
}
for (Register use : instruction.uses()) {
if (!block.varKill.isMember(use))
block.UEVar.add(use);
}
if (instruction.definesVar()) {
Register def = instruction.def();
block.varKill.set(def.id);
block.varKill.add(def);
}
}
}
Expand All @@ -51,15 +49,15 @@ private void computeLiveness(List<BasicBlock> blocks) {
}

private boolean recomputeLiveOut(BasicBlock block) {
BitSet oldLiveOut = (BitSet) block.liveOut.clone();
LiveSet oldLiveOut = block.liveOut.dup();
for (BasicBlock m: block.successors) {
BitSet mLiveIn = (BitSet) m.liveOut.clone();
LiveSet mLiveIn = m.liveOut.dup();
// LiveOut(m) intersect not VarKill(m)
mLiveIn.andNot(m.varKill);
mLiveIn.intersectNot(m.varKill);
// UEVar(m) union (LiveOut(m) intersect not VarKill(m))
mLiveIn.or(m.UEVar);
mLiveIn.union(m.UEVar);
// LiveOut(block) =union (UEVar(m) union (LiveOut(m) intersect not VarKill(m)))
block.liveOut.or(mLiveIn);
block.liveOut.union(mLiveIn);
}
return !oldLiveOut.equals(block.liveOut);
}
Expand Down
Loading
Loading