Skip to content

Commit 1acef5f

Browse files
#12 Update to Liveness calculation to handle phis correctly (hopefully).
1 parent 9a5b42f commit 1acef5f

File tree

6 files changed

+197
-29
lines changed

6 files changed

+197
-29
lines changed

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

+17-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ public class BasicBlock {
6161
* head of some block that is a successor of this block.
6262
*/
6363
LiveSet liveOut;
64+
65+
/**
66+
* Inputs to successor block's phi function
67+
*/
68+
LiveSet phiUses;
69+
/**
70+
* Phi definitions in this block
71+
*/
72+
LiveSet phiDefs;
73+
/**
74+
* Live in set
75+
*/
76+
LiveSet liveIn;
6477
// -----------------------
6578

6679
public BasicBlock(int bid, boolean loopHead) {
@@ -92,7 +105,7 @@ public void addSuccessor(BasicBlock successor) {
92105
public void insertPhiFor(Register var) {
93106
for (Instruction i: instructions) {
94107
if (i instanceof Instruction.Phi phi) {
95-
if (phi.def().nonSSAId() == var.nonSSAId())
108+
if (phi.value().nonSSAId() == var.nonSSAId())
96109
// already added
97110
return;
98111
}
@@ -124,8 +137,11 @@ public static StringBuilder toStr(StringBuilder sb, BasicBlock bb, BitSet visite
124137
n.toStr(sb).append("\n");
125138
}
126139
if (dumpLiveness) {
140+
if (bb.phiDefs != null) sb.append(" #PHIDEFS = ").append(bb.phiDefs.toString()).append("\n");
141+
if (bb.phiUses != null) sb.append(" #PHIUSES = ").append(bb.phiUses.toString()).append("\n");
127142
if (bb.UEVar != null) sb.append(" #UEVAR = ").append(bb.UEVar.toString()).append("\n");
128143
if (bb.varKill != null) sb.append(" #VARKILL = ").append(bb.varKill.toString()).append("\n");
144+
if (bb.liveIn != null) sb.append(" #LIVEIN = ").append(bb.liveIn.toString()).append("\n");
129145
if (bb.liveOut != null) sb.append(" #LIVEOUT = ").append(bb.liveOut.toString()).append("\n");
130146
}
131147
for (BasicBlock succ: bb.successors) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private void scheduleCopies(BasicBlock block, List<Integer> pushed) {
8484
for (BasicBlock s: block.successors) {
8585
int j = SSATransform.whichPred(s, block);
8686
for (Instruction.Phi phi: s.phis()) {
87-
Register dst = phi.def();
87+
Register dst = phi.value();
8888
Register src = phi.input(j); // jth operand of phi node
8989
copySet.add(new CopyItem(src, dst));
9090
map.put(src.id, src);
@@ -166,7 +166,7 @@ private void insertAfterPhi(BasicBlock bb, Register phiDef, Instruction newInst)
166166
for (int pos = 0; pos < bb.instructions.size(); pos++) {
167167
Instruction i = bb.instructions.get(pos);
168168
if (i instanceof Instruction.Phi phi) {
169-
if (phi.def().id == phiDef.id) {
169+
if (phi.value().id == phiDef.id) {
170170
insertionPos = pos+1; // After phi
171171
break;
172172
}

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

+26-4
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,16 @@ public StringBuilder toStr(StringBuilder sb) {
337337
}
338338

339339
/**
340-
* Phi does not generate uses.
340+
* Phi does not generate uses or defs directly, instead
341+
* they are treated as a special case.
342+
* To avoid bugs we do not use the def or uses.
341343
*/
342344
public static class Phi extends Instruction {
345+
public Register value;
343346
public final Register[] inputs;
344-
public Phi(Register dest, List<Register> inputs) {
345-
super(I_PHI, new Operand.RegisterOperand(dest));
347+
public Phi(Register value, List<Register> inputs) {
348+
super(I_PHI);
349+
this.value = value;
346350
this.inputs = inputs.toArray(new Register[inputs.size()]);
347351
}
348352
public void replaceInput(int i, Register newReg) {
@@ -352,8 +356,26 @@ public Register input(int i) {
352356
return inputs[i];
353357
}
354358
@Override
359+
public Register def() {
360+
throw new UnsupportedOperationException();
361+
}
362+
@Override
363+
public void replaceDef(Register newReg) {
364+
throw new UnsupportedOperationException();
365+
}
366+
@Override
367+
public boolean definesVar() {
368+
return false;
369+
}
370+
public Register value() {
371+
return value;
372+
}
373+
public void replaceValue(Register newReg) {
374+
this.value = newReg;
375+
}
376+
@Override
355377
public StringBuilder toStr(StringBuilder sb) {
356-
sb.append(def).append(" = phi(");
378+
sb.append(value().name()).append(" = phi(");
357379
for (int i = 0; i < inputs.length; i++) {
358380
if (i > 0) sb.append(", ");
359381
sb.append(inputs[i].name());

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

+57-14
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,81 @@
44

55
/**
66
* Compute LiveOut for each Basic Block
7-
* Implementation is based on description in 'Engineering a Compiler' 2nd ed.
7+
*
8+
* Original Implementation was based on description in 'Engineering a Compiler' 2nd ed.
89
* pages 446-447.
910
*
10-
* It turns out that this dataflow implementation cannot correctly handle
11+
* It turned out that this dataflow implementation cannot correctly handle
1112
* phis, because with phis, the inputs are live at the predecessor blocks.
1213
* We have to look at alternative approaches when input is SSA form.
1314
* Surprisingly even with this approach, the lost copy and swap problems
14-
* appear to work correctly.
15+
* appeared to work correctly.
16+
*
17+
* The new approach is based on formula described in
18+
* Computing Liveness Sets for SSA-Form Programs
19+
* Florian Brandner, Benoit Boissinot, Alain Darte, Benoît Dupont de Dinechin, Fabrice Rastello
20+
*
21+
* The implementation is the unoptimized simple one.
22+
* However, we have a modification to ensure that if we see a block
23+
* which loops to itself and has Phi cycles, then the Phi is only added to
24+
* PhiDefs.
1525
*/
1626
public class Liveness {
1727

1828
public Liveness(CompiledFunction function) {
1929
List<BasicBlock> blocks = BBHelper.findAllBlocks(function.entry);
2030
RegisterPool regPool = function.registerPool;
21-
init(regPool, blocks);
31+
initBlocks(regPool, blocks);
32+
init(blocks);
2233
computeLiveness(blocks);
2334
function.hasLiveness = true;
2435
}
2536

26-
private void init(RegisterPool regPool, List<BasicBlock> blocks) {
37+
private void initBlocks(RegisterPool regPool, List<BasicBlock> blocks) {
2738
int numRegisters = regPool.numRegisters();
2839
for (BasicBlock block : blocks) {
2940
block.UEVar = new LiveSet(numRegisters);
3041
block.varKill = new LiveSet(numRegisters);
3142
block.liveOut = new LiveSet(numRegisters);
43+
block.liveIn = new LiveSet(numRegisters);
44+
block.phiUses = new LiveSet(numRegisters);
45+
block.phiDefs = new LiveSet(numRegisters);
46+
}
47+
}
48+
49+
private void init(List<BasicBlock> blocks) {
50+
for (BasicBlock block : blocks) {
51+
// We st up phiDefs first because when we
52+
// look at phi uses we need to refer back here
53+
// see comments on phi cycles below
54+
for (Instruction instruction : block.instructions) {
55+
if (instruction instanceof Instruction.Phi phi) {
56+
block.phiDefs.add(phi.value());
57+
}
58+
else break;
59+
}
3260
for (Instruction instruction : block.instructions) {
3361
for (Register use : instruction.uses()) {
3462
if (!block.varKill.isMember(use))
3563
block.UEVar.add(use);
3664
}
37-
if (instruction.definesVar()) {
65+
if (instruction.definesVar() && !(instruction instanceof Instruction.Phi)) {
3866
Register def = instruction.def();
3967
block.varKill.add(def);
4068
}
69+
if (instruction instanceof Instruction.Phi phi) {
70+
for (int i = 0; i < block.predecessors.size(); i++) {
71+
BasicBlock pred = block.predecessors.get(i);
72+
Register use = phi.input(i);
73+
// We can have a block referring it its own phis
74+
// if there is loop back and there are cycles
75+
// such as e.g. the swap copy problem
76+
if (pred == block &&
77+
block.phiDefs.isMember(use))
78+
continue;
79+
pred.phiUses.add(use);
80+
}
81+
}
4182
}
4283
}
4384
}
@@ -53,17 +94,19 @@ private void computeLiveness(List<BasicBlock> blocks) {
5394
}
5495
}
5596

97+
// See 'Computing Liveness Sets for SSA-Form Programs'
98+
// LiveIn(B) = PhiDefs(B) U UpwardExposed(B) U (LiveOut(B) \ Defs(B))
99+
// LiveOut(B) = U all S (LiveIn(S) \ PhiDefs(S)) U PhiUses(B)
56100
private boolean recomputeLiveOut(BasicBlock block) {
57101
LiveSet oldLiveOut = block.liveOut.dup();
58-
for (BasicBlock m: block.successors) {
59-
LiveSet mLiveIn = m.liveOut.dup();
60-
// LiveOut(m) intersect not VarKill(m)
61-
mLiveIn.intersectNot(m.varKill);
62-
// UEVar(m) union (LiveOut(m) intersect not VarKill(m))
63-
mLiveIn.union(m.UEVar);
64-
// LiveOut(block) =union (UEVar(m) union (LiveOut(m) intersect not VarKill(m)))
65-
block.liveOut.union(mLiveIn);
102+
LiveSet t = block.liveOut.dup().intersectNot(block.varKill);
103+
block.liveIn.union(block.phiDefs).union(block.UEVar).union(t);
104+
block.liveOut.clear();
105+
for (BasicBlock s: block.successors) {
106+
t = s.liveIn.dup().intersectNot(s.phiDefs);
107+
block.liveOut.union(t);
66108
}
109+
block.liveOut.union(block.phiUses);
67110
return !oldLiveOut.equals(block.liveOut);
68111
}
69112
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ Register makeVersion(Register reg) {
115115
void search(BasicBlock block) {
116116
// Replace v = phi(...) with v_i = phi(...)
117117
for (Instruction.Phi phi: block.phis()) {
118-
Register ssaReg = makeVersion(phi.def());
119-
phi.replaceDef(ssaReg);
118+
Register ssaReg = makeVersion(phi.value());
119+
phi.replaceValue(ssaReg);
120120
}
121121
// for each instruction v = x op y
122122
// first replace x,y

0 commit comments

Comments
 (0)