Skip to content

Commit 4ec727b

Browse files
Merge pull request #7 from CompilerProgramming/outofssa
Out of SSA
2 parents 3e041c5 + 627ed2a commit 4ec727b

File tree

5 files changed

+399
-2
lines changed

5 files changed

+399
-2
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ public class CompiledFunction {
2323

2424
private final int frameSlots;
2525

26+
public boolean isSSA;
27+
public boolean hasLiveness;
28+
2629
/**
2730
* We essentially do a form of abstract interpretation as we generate
2831
* the bytecode instructions. For this purpose we use a virtual operand stack.
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package com.compilerprogramming.ezlang.compiler;
2+
3+
import java.util.*;
4+
5+
/**
6+
* Converts from SSA form to non-SSA form.
7+
* Implementation is based on description in
8+
* 'Practical Improvements to the Construction and Destruction
9+
* of Static Single Assignment Form' by Preston Briggs.
10+
*/
11+
public class ExitSSA {
12+
13+
CompiledFunction function;
14+
NameStack[] stacks;
15+
DominatorTree tree;
16+
17+
public ExitSSA(CompiledFunction function) {
18+
this.function = function;
19+
if (!function.isSSA) throw new IllegalStateException();
20+
if (!function.hasLiveness) {
21+
new Liveness().computeLiveness(function);
22+
}
23+
tree = new DominatorTree(function.entry);
24+
initStack();
25+
insertCopies(function.entry);
26+
removePhis();
27+
}
28+
29+
private void removePhis() {
30+
for (BasicBlock block : tree.blocks) {
31+
block.instructions.removeIf(instruction -> instruction instanceof Instruction.Phi);
32+
}
33+
}
34+
35+
/* Algorithm for iterating through blocks to perform phi replacement */
36+
private void insertCopies(BasicBlock block) {
37+
List<Register> pushed = new ArrayList<>();
38+
for (Instruction i: block.instructions) {
39+
// replace all uses u with stacks[i]
40+
if (i.usesVars()) {
41+
replaceUses(i);
42+
}
43+
}
44+
scheduleCopies(block, pushed);
45+
for (BasicBlock c: block.dominatedChildren) {
46+
insertCopies(c);
47+
}
48+
for (Register name: pushed) {
49+
stacks[name.id].pop();
50+
}
51+
}
52+
53+
/**
54+
* replace all uses u with stacks[i]
55+
*/
56+
private void replaceUses(Instruction i) {
57+
var oldUses = i.uses();
58+
Register[] newUses = new Register[oldUses.size()];
59+
for (int u = 0; u < oldUses.size(); u++) {
60+
Register use = oldUses.get(u);
61+
if (!stacks[use.id].isEmpty())
62+
newUses[u] = stacks[use.id].top();
63+
else
64+
newUses[u] = use;
65+
}
66+
i.replaceUses(newUses);
67+
}
68+
69+
static class CopyItem {
70+
Register src;
71+
Register dest;
72+
boolean removed;
73+
74+
public CopyItem(Register src, Register dest) {
75+
this.src = src;
76+
this.dest = dest;
77+
this.removed = false;
78+
}
79+
}
80+
81+
private void scheduleCopies(BasicBlock block, List<Register> pushed) {
82+
/* Pass 1 - Initialize data structures */
83+
/* In this pass we count the number of times a name is used by other phi-nodes */
84+
List<CopyItem> copySet = new ArrayList<>();
85+
Map<Integer, Register> map = new HashMap<>();
86+
BitSet usedByAnother = new BitSet(function.registerPool.numRegisters()*2);
87+
for (BasicBlock s: block.successors) {
88+
int j = SSATransform.whichPred(s, block);
89+
for (Instruction.Phi phi: s.phis()) {
90+
Register dst = phi.def();
91+
Register src = phi.inputs.get(j).reg; // jth operand of phi node
92+
copySet.add(new CopyItem(src, dst));
93+
map.put(src.id, src);
94+
map.put(dst.id, dst);
95+
usedByAnother.set(src.id);
96+
}
97+
}
98+
99+
/* Pass 2: setup up the worklist of initial copies */
100+
/* In this pass we build a worklist of names that are not used in other phi nodes */
101+
List<CopyItem> workList = new ArrayList<>();
102+
for (CopyItem copyItem: copySet) {
103+
if (!usedByAnother.get(copyItem.dest.id)) {
104+
copyItem.removed = true;
105+
workList.add(copyItem);
106+
}
107+
}
108+
copySet.removeIf(copyItem -> copyItem.removed);
109+
110+
/* Pass 3: iterate over the worklist, inserting copies */
111+
/* Copy operations whose destinations are not used by other copy operations can be scheduled immediately */
112+
/* Each time we insert a copy operation we add the source of that op to the worklist */
113+
while (!workList.isEmpty() || !copySet.isEmpty()) {
114+
while (!workList.isEmpty()) {
115+
CopyItem copyItem = workList.remove(0);
116+
Register src = copyItem.src;
117+
Register dest = copyItem.dest;
118+
if (block.liveOut.get(dest.id)) {
119+
/* Insert a copy from dest to a new temp t at phi node defining dest */
120+
Register t = insertCopy(block, dest);
121+
stacks[dest.id].push(t);
122+
pushed.add(t);
123+
}
124+
/* Insert a copy operation from map[src] to dst at end of BB */
125+
appendCopy(block, map.get(src.id), dest);
126+
map.put(src.id, dest);
127+
/* If src is the name of a dest in copySet add item to worklist */
128+
CopyItem item = isDest(copySet, src);
129+
if (item != null) {
130+
workList.add(item);
131+
}
132+
}
133+
if (!copySet.isEmpty()) {
134+
CopyItem copyItem = copySet.remove(0);
135+
/* Insert a copy from dst to new temp at the end of Block */
136+
Register t = appendCopy(block, copyItem.dest);
137+
map.put(copyItem.dest.id, t);
138+
workList.add(copyItem);
139+
}
140+
}
141+
}
142+
143+
private void insertAtEnd(BasicBlock bb, Instruction i) {
144+
assert bb.instructions.size() > 0;
145+
int pos = bb.instructions.size()-1;
146+
bb.instructions.add(pos, i);
147+
}
148+
149+
private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst) {
150+
assert bb.instructions.size() > 0;
151+
int pos = 0;
152+
while (pos < bb.instructions.size()) {
153+
Instruction i = bb.instructions.get(pos);
154+
if (i instanceof Instruction.Phi phi &&
155+
phi.def().id == phiDef.id) {
156+
pos += 1;
157+
break;
158+
}
159+
}
160+
if (pos == bb.instructions.size()) {
161+
throw new IllegalStateException();
162+
}
163+
bb.instructions.add(pos, newInst);
164+
}
165+
166+
167+
/* Insert a copy from dest to new temp at end of BB, and return temp */
168+
private Register appendCopy(BasicBlock block, Register dest) {
169+
var temp = function.registerPool.newReg(dest.name(), dest.type);
170+
var inst = new Instruction.Move(new Operand.RegisterOperand(dest), new Operand.RegisterOperand(temp));
171+
insertAtEnd(block, inst);
172+
return temp;
173+
}
174+
175+
/* If src is the name of a dest in copySet return the item */
176+
private CopyItem isDest(List<CopyItem> copySet, Register src) {
177+
for (CopyItem copyItem: copySet) {
178+
if (copyItem.dest.id == src.id)
179+
return copyItem;
180+
}
181+
return null;
182+
}
183+
184+
/* Insert a copy from src to dst at end of BB */
185+
private void appendCopy(BasicBlock block, Register src, Register dest) {
186+
var inst = new Instruction.Move(new Operand.RegisterOperand(src), new Operand.RegisterOperand(dest));
187+
insertAtEnd(block, inst);
188+
}
189+
190+
/* Insert a copy dest to a new temp at phi node defining dest, return temp */
191+
private Register insertCopy(BasicBlock block, Register dst) {
192+
var temp = function.registerPool.newReg(dst.name(), dst.type);
193+
var inst = new Instruction.Move(new Operand.RegisterOperand(dst), new Operand.RegisterOperand(temp));
194+
insertAfterPhi(block, dst, inst);
195+
return temp;
196+
}
197+
198+
private void initStack() {
199+
stacks = new NameStack[function.registerPool.numRegisters()];
200+
for (int i = 0; i < stacks.length; i++)
201+
stacks[i] = new NameStack();
202+
}
203+
204+
static class NameStack {
205+
List<Register> stack = new ArrayList<>();
206+
void push(Register r) { stack.add(r); }
207+
Register top() { return stack.getLast(); }
208+
void pop() { stack.removeLast(); }
209+
boolean isEmpty() { return stack.isEmpty(); }
210+
}
211+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public void computeLiveness(CompiledFunction function) {
1515
RegisterPool regPool = function.registerPool;
1616
init(regPool, blocks);
1717
computeLiveness(blocks);
18+
function.hasLiveness = true;
1819
}
1920

2021
private void init(RegisterPool regPool, List<BasicBlock> blocks) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public SSATransform(CompiledFunction bytecodeFunction) {
3030
findGlobalVars();
3131
insertPhis();
3232
renameVars();
33+
bytecodeFunction.isSSA = true;
34+
bytecodeFunction.hasLiveness = false;
3335
}
3436

3537
private void computeDomTreeAndDominanceFrontiers() {
@@ -161,7 +163,7 @@ void search(BasicBlock block) {
161163
}
162164
}
163165

164-
private int whichPred(BasicBlock s, BasicBlock block) {
166+
public static int whichPred(BasicBlock s, BasicBlock block) {
165167
int i = 0;
166168
for (BasicBlock p: s.predecessors) {
167169
if (p == block)

0 commit comments

Comments
 (0)