Skip to content

Commit 1a934a7

Browse files
Merge pull request #31 from CompilerProgramming/develop
Add test for simple SSA liveness example, also add comments and reorg…
2 parents 8fbe6e4 + edc687b commit 1a934a7

File tree

3 files changed

+164
-16
lines changed

3 files changed

+164
-16
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,12 @@ public class BasicBlock {
6767
LiveSet liveOut;
6868

6969
/**
70-
* Inputs to successor block's phi function
70+
* phiUses(B) is the set of variables used in a phi-operation at entry of a block successor of the block B
71+
* That is, inputs to successor block's phi functions.
7172
*/
7273
LiveSet phiUses;
7374
/**
74-
* Phi definitions in this block
75+
* phiDefs(B) the variables defined by phi-operations at entry of the block B
7576
*/
7677
LiveSet phiDefs;
7778
/**

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

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99
* pages 446-447.
1010
*
1111
* It turned out that this dataflow implementation cannot correctly handle
12-
* phis, because with phis, the inputs are live at the predecessor blocks.
13-
* We have to look at alternative approaches when input is SSA form.
14-
* Surprisingly even with this approach, the lost copy and swap problems
12+
* phis. Therefore, we have to look at alternative approaches when input is SSA form.
13+
* Surprisingly even with the flawed approach, the lost copy and swap problems
1514
* appeared to work correctly.
1615
*
16+
* Phis need special considerations:
17+
*
18+
* Phi inputs are live out at the predecessor blocks, but not live in for phi block.
19+
* Phi def is live in at phi block but not in live out at predecessor blocks.
20+
*
1721
* The new approach is based on formula described in
1822
* Computing Liveness Sets for SSA-Form Programs
1923
* Florian Brandner, Benoit Boissinot, Alain Darte, Benoît Dupont de Dinechin, Fabrice Rastello
@@ -48,25 +52,29 @@ private void initBlocks(RegisterPool regPool, List<BasicBlock> blocks) {
4852

4953
private void init(List<BasicBlock> blocks) {
5054
for (BasicBlock block : blocks) {
51-
// We st up phiDefs first because when we
55+
// Any vars created by phi instructions are added to phiDefs
56+
// for this block; these vars will be live on entry to block
57+
// but not live out from predecessor blocks.
58+
//
59+
// We set up phiDefs first because when we
5260
// look at phi uses we need to refer back here
5361
// see comments on phi cycles below
5462
for (Instruction instruction : block.instructions) {
5563
if (instruction instanceof Instruction.Phi phi) {
5664
block.phiDefs.add(phi.value());
5765
}
66+
// There is a scenario where other instructions can appear
67+
// between phi instructions - this happens during the SSA deconstruction
68+
// using Brigg's method. But we don't calculate liveness
69+
// in the middle of that process, so assuming all phis are together
70+
// at the top of the block is okay
5871
else break;
5972
}
6073
for (Instruction instruction : block.instructions) {
61-
for (Register use : instruction.uses()) {
62-
if (!block.varKill.contains(use))
63-
block.UEVar.add(use);
64-
}
65-
if (instruction.definesVar() && !(instruction instanceof Instruction.Phi)) {
66-
Register def = instruction.def();
67-
block.varKill.add(def);
68-
}
6974
if (instruction instanceof Instruction.Phi phi) {
75+
// Any uses in a Phi are added to the phiUses of predecessor
76+
// block. These uses will be in live out of predecessor block but
77+
// not live in for current block.
7078
for (int i = 0; i < block.predecessors.size(); i++) {
7179
BasicBlock pred = block.predecessors.get(i);
7280
if (!phi.isRegisterInput(i))
@@ -76,11 +84,24 @@ private void init(List<BasicBlock> blocks) {
7684
// if there is loop back and there are cycles
7785
// such as e.g. the swap copy problem
7886
if (pred == block &&
79-
block.phiDefs.contains(use))
87+
block.phiDefs.contains(use))
8088
continue;
8189
pred.phiUses.add(use);
8290
}
8391
}
92+
else {
93+
// Non phi instructions follow regular
94+
// logic. Any var that is used before being defined
95+
// is added to upward expose set.
96+
for (Register use : instruction.uses()) {
97+
if (!block.varKill.contains(use))
98+
block.UEVar.add(use);
99+
}
100+
if (instruction.definesVar()) {
101+
Register def = instruction.def();
102+
block.varKill.add(def);
103+
}
104+
}
84105
}
85106
}
86107
}
@@ -89,6 +110,7 @@ private void computeLiveness(List<BasicBlock> blocks) {
89110
boolean changed = true;
90111
while (changed) {
91112
changed = false;
113+
// TODO we should process in RPO order
92114
for (BasicBlock block : blocks) {
93115
if (recomputeLiveOut(block))
94116
changed = true;
@@ -97,8 +119,17 @@ private void computeLiveness(List<BasicBlock> blocks) {
97119
}
98120

99121
// See 'Computing Liveness Sets for SSA-Form Programs'
122+
//
100123
// LiveIn(B) = PhiDefs(B) U UpwardExposed(B) U (LiveOut(B) \ Defs(B))
101-
// LiveOut(B) = U all S (LiveIn(S) \ PhiDefs(S)) U PhiUses(B)
124+
// LiveOut(B) = U all S (LiveIn(S) \ PhiDefs(S)) U PhiUses
125+
//
126+
// For a phi-function a_0 = phi(a_1, ..., a_n ) in block B_0, where a_i comes from block B_i, then:
127+
// * a_0 is considered to be live-in for B_0, but, with respect to this phi-function, it is
128+
// not live-out for B_i, i > 0.
129+
// * a_i, i > 0, is considered to be live-out of B_i , but, with respect to this phi-function,
130+
// it is not live-in for B_0.
131+
// This corresponds to placing a copy of a_i to a_0 on each edge from B_i to B_0.
132+
//
102133
private boolean recomputeLiveOut(BasicBlock block) {
103134
LiveSet oldLiveOut = block.liveOut.dup();
104135
LiveSet t = block.liveOut.dup().subtract(block.varKill);

optvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestLiveness.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,4 +444,120 @@ func foo(p: Int)->Int
444444
""", actual);
445445
}
446446

447+
@Test
448+
public void testSimpleCase() {
449+
String src = """
450+
func foo()->Int
451+
{
452+
return 1 && 2
453+
}
454+
""";
455+
var typeDict = compileSrc(src);
456+
var funcSymbol = typeDict.lookup("foo");
457+
CompiledFunction func = (CompiledFunction) ((Symbol.FunctionTypeSymbol) funcSymbol).code();
458+
func.livenessAnalysis();
459+
StringBuilder result = new StringBuilder();
460+
result.append("Pre-SSA\n");
461+
func.toStr(result, true);
462+
new EnterSSA(func, Options.NONE);
463+
func.livenessAnalysis();
464+
result.append("Post-SSA\n");
465+
func.toStr(result, true);
466+
Assert.assertEquals("""
467+
Pre-SSA
468+
func foo()->Int
469+
Reg #0 %t0
470+
L0:
471+
if 1 goto L2 else goto L3
472+
#PHIDEFS = {}
473+
#PHIUSES = {}
474+
#UEVAR = {}
475+
#VARKILL = {}
476+
#LIVEIN = {}
477+
#LIVEOUT = {}
478+
L2:
479+
%t0 = 2
480+
goto L4
481+
#PHIDEFS = {}
482+
#PHIUSES = {}
483+
#UEVAR = {}
484+
#VARKILL = {0}
485+
#LIVEIN = {}
486+
#LIVEOUT = {0}
487+
L4:
488+
ret %t0
489+
goto L1
490+
#PHIDEFS = {}
491+
#PHIUSES = {}
492+
#UEVAR = {0}
493+
#VARKILL = {}
494+
#LIVEIN = {0}
495+
#LIVEOUT = {}
496+
L1:
497+
#PHIDEFS = {}
498+
#PHIUSES = {}
499+
#UEVAR = {}
500+
#VARKILL = {}
501+
#LIVEIN = {}
502+
#LIVEOUT = {}
503+
L3:
504+
%t0 = 0
505+
goto L4
506+
#PHIDEFS = {}
507+
#PHIUSES = {}
508+
#UEVAR = {}
509+
#VARKILL = {0}
510+
#LIVEIN = {}
511+
#LIVEOUT = {0}
512+
Post-SSA
513+
func foo()->Int
514+
Reg #0 %t0
515+
Reg #1 %t0_0
516+
Reg #2 %t0_1
517+
Reg #3 %t0_2
518+
L0:
519+
if 1 goto L2 else goto L3
520+
#PHIDEFS = {}
521+
#PHIUSES = {}
522+
#UEVAR = {}
523+
#VARKILL = {}
524+
#LIVEIN = {}
525+
#LIVEOUT = {}
526+
L2:
527+
%t0_1 = 2
528+
goto L4
529+
#PHIDEFS = {}
530+
#PHIUSES = {2}
531+
#UEVAR = {}
532+
#VARKILL = {2}
533+
#LIVEIN = {}
534+
#LIVEOUT = {2}
535+
L4:
536+
%t0_2 = phi(%t0_1, %t0_0)
537+
ret %t0_2
538+
goto L1
539+
#PHIDEFS = {3}
540+
#PHIUSES = {}
541+
#UEVAR = {3}
542+
#VARKILL = {}
543+
#LIVEIN = {3}
544+
#LIVEOUT = {}
545+
L1:
546+
#PHIDEFS = {}
547+
#PHIUSES = {}
548+
#UEVAR = {}
549+
#VARKILL = {}
550+
#LIVEIN = {}
551+
#LIVEOUT = {}
552+
L3:
553+
%t0_0 = 0
554+
goto L4
555+
#PHIDEFS = {}
556+
#PHIUSES = {1}
557+
#UEVAR = {}
558+
#VARKILL = {1}
559+
#LIVEIN = {}
560+
#LIVEOUT = {1}
561+
""", result.toString());
562+
}
447563
}

0 commit comments

Comments
 (0)