From 807198113ba1fcec81709e4d5df4b8f1b8f92c2a Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Mon, 8 May 2017 18:08:07 +0200 Subject: [PATCH] SSA with phi function almost complete --- .../camelot64/kickc/icl/ConstantInteger.java | 6 +- .../camelot64/kickc/icl/ControlFlowBlock.java | 10 +- .../camelot64/kickc/icl/ControlFlowGraph.java | 5 + ...assGenerateSingleStaticAssignmentForm.java | 107 ++++++++++++++++++ .../icl/PassGenerateStatementSequence.java | 5 + .../kickc/icl/PassTypeInference.java | 2 +- .../kickc/icl/StatementAssignment.java | 13 +++ src/dk/camelot64/kickc/icl/StatementPhi.java | 49 ++++++++ src/dk/camelot64/kickc/icl/Symbol.java | 49 ++++++-- src/dk/camelot64/kickc/icl/SymbolManager.java | 7 +- src/dk/camelot64/kickc/test/main.java | 21 ++-- src/dk/camelot64/kickc/test/test.kc | 3 + 12 files changed, 254 insertions(+), 23 deletions(-) create mode 100644 src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java create mode 100644 src/dk/camelot64/kickc/icl/StatementPhi.java diff --git a/src/dk/camelot64/kickc/icl/ConstantInteger.java b/src/dk/camelot64/kickc/icl/ConstantInteger.java index 0b42a91e2..38aff41a4 100644 --- a/src/dk/camelot64/kickc/icl/ConstantInteger.java +++ b/src/dk/camelot64/kickc/icl/ConstantInteger.java @@ -15,8 +15,12 @@ public class ConstantInteger implements Constant { return number; } + public SymbolType getType() { + return PassTypeInference.inferType(this); + } + @Override public String toString() { - return Integer.toString(number); + return "("+getType().getTypeName()+") "+Integer.toString(number); } } diff --git a/src/dk/camelot64/kickc/icl/ControlFlowBlock.java b/src/dk/camelot64/kickc/icl/ControlFlowBlock.java index 43934d202..7bdb21cba 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowBlock.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowBlock.java @@ -65,13 +65,17 @@ public class ControlFlowBlock { return statements; } + public void addPhiStatement(Symbol newVersion) { + statements.add(0, new StatementPhi(newVersion)); + } + @Override public String toString() { StringBuffer out = new StringBuffer(); - out.append(label.getName() + ":" + "\n"); - out.append(" from:"); + out.append(label.getName() + ":" ); + out.append(" from"); for (ControlFlowBlock predecessor : predecessors) { - out.append(predecessor.getLabel().getName()+" "); + out.append(" "+predecessor.getLabel().getName()); } out.append("\n"); for (Statement statement : statements) { diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java index 112ab3ef6..6ea6c0777 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java @@ -1,5 +1,6 @@ package dk.camelot64.kickc.icl; +import java.util.Collection; import java.util.Map; /** The control flow graph of the program. @@ -31,4 +32,8 @@ public class ControlFlowGraph { } return out.toString(); } + + public Collection getAllBlocks() { + return blocks.values(); + } } diff --git a/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java b/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java new file mode 100644 index 000000000..f6e58b996 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java @@ -0,0 +1,107 @@ +package dk.camelot64.kickc.icl; + +import java.util.HashMap; +import java.util.Map; + +/** + * Compiler Pass that generates Single Static Assignment Form based on a Control Flow Graph. + *

First versions all variable assignments, then versions all variable usages and introduces necessary Phi-functions, + *

See https://en.wikipedia.org/wiki/Static_single_assignment_form + */ +public class PassGenerateSingleStaticAssignmentForm { + + private SymbolManager symbols; + private ControlFlowGraph controlFlowGraph; + + public PassGenerateSingleStaticAssignmentForm(SymbolManager symbols, ControlFlowGraph controlFlowGraph) { + this.symbols = symbols; + this.controlFlowGraph = controlFlowGraph; + } + + public void generate() { + versionAllAssignments(); + versionAllUses(); + } + + + /** + * Version all non-versioned non-intermediary being assigned a value. + */ + private void versionAllAssignments() { + for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { + for (Statement statement : block.getStatements()) { + if (statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + Symbol assignedSymbol = (Symbol) assignment.getlValue(); + if (!assignedSymbol.isIntermediate() && !assignedSymbol.isVersioned()) { + // Assignment to a non-versioned non-intermediary variable + Symbol version = symbols.createVersion(assignedSymbol); + assignment.setLValue(version); + } + } + } + } + } + + /** + * Version all uses of non-versioned non-intermediary variables + */ + private void versionAllUses() { + for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { + // Newest version of variables in the block. + Map blockVersions = new HashMap<>(); + // New phi functions introduced in the block to create versions of variables. + Map blockNewPhis = new HashMap<>(); + for (Statement statement : block.getStatements()) { + if (statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + { + Symbol version = findOrCreateVersion(assignment.getRValue1(), blockVersions, blockNewPhis); + if (version != null) { + assignment.setRValue1(version); + } + } + { + Symbol version = findOrCreateVersion(assignment.getRValue2(), blockVersions, blockNewPhis); + if (version != null) { + assignment.setRValue2(version); + } + } + // Update map of versions encountered in the block + Symbol lSymbol = (Symbol) assignment.getlValue(); + if (lSymbol.isVersioned()) { + blockVersions.put(lSymbol.getVersionOf(), lSymbol); + } + } + } + // Add new Phi functions to block + for (Symbol symbol : blockNewPhis.keySet()) { + block.addPhiStatement(blockNewPhis.get(symbol)); + } + } + + } + + private Symbol findOrCreateVersion(RValue rValue, Map blockVersions, Map blockNewPhis) { + Symbol version = null; + if (rValue instanceof Symbol) { + Symbol rSymbol = (Symbol) rValue; + if (!rSymbol.isIntermediate() && !rSymbol.isVersioned()) { + // rValue needs versioning - look for version in statements + version = blockVersions.get(rSymbol); + if (version == null) { + // look for version in new phi functions + version = blockNewPhis.get(rSymbol); + } + if (version == null) { + // create a new phi function + version = symbols.createVersion(rSymbol); + blockNewPhis.put(rSymbol, version); + } + } + } + return version; + } + + +} diff --git a/src/dk/camelot64/kickc/icl/PassGenerateStatementSequence.java b/src/dk/camelot64/kickc/icl/PassGenerateStatementSequence.java index a0fe3f05e..a5f4cbd6c 100644 --- a/src/dk/camelot64/kickc/icl/PassGenerateStatementSequence.java +++ b/src/dk/camelot64/kickc/icl/PassGenerateStatementSequence.java @@ -15,6 +15,10 @@ public class PassGenerateStatementSequence extends KickCBaseVisitor { this.sequence = new StatementSequence(); } + public void generate(KickCParser.FileContext file) { + this.visit(file); + } + @Override public RValue visitFile(KickCParser.FileContext ctx) { this.visit(ctx.stmtSeq()); @@ -169,4 +173,5 @@ public class PassGenerateStatementSequence extends KickCBaseVisitor { public SymbolManager getSymbols() { return this.symbolManager; } + } diff --git a/src/dk/camelot64/kickc/icl/PassTypeInference.java b/src/dk/camelot64/kickc/icl/PassTypeInference.java index 236519844..ad020e7b9 100644 --- a/src/dk/camelot64/kickc/icl/PassTypeInference.java +++ b/src/dk/camelot64/kickc/icl/PassTypeInference.java @@ -61,7 +61,7 @@ public class PassTypeInference { } } - private SymbolType inferType(RValue rValue) { + public static SymbolType inferType(RValue rValue) { SymbolType type = SymbolType.VAR; if (rValue instanceof Symbol) { Symbol rSymbol = (Symbol) rValue; diff --git a/src/dk/camelot64/kickc/icl/StatementAssignment.java b/src/dk/camelot64/kickc/icl/StatementAssignment.java index 45c2b9da7..3c4687490 100644 --- a/src/dk/camelot64/kickc/icl/StatementAssignment.java +++ b/src/dk/camelot64/kickc/icl/StatementAssignment.java @@ -42,10 +42,18 @@ public class StatementAssignment implements Statement { return lValue; } + public void setLValue(LValue lValue) { + this.lValue = lValue; + } + public RValue getRValue1() { return rValue1; } + public void setRValue1(RValue rValue1) { + this.rValue1 = rValue1; + } + public Operator getOperator() { return operator; } @@ -54,6 +62,10 @@ public class StatementAssignment implements Statement { return rValue2; } + public void setRValue2(RValue rValue2) { + this.rValue2 = rValue2; + } + @Override public String toString() { return @@ -63,4 +75,5 @@ public class StatementAssignment implements Statement { (operator==null?"":operator+" ") + rValue2 ; } + } diff --git a/src/dk/camelot64/kickc/icl/StatementPhi.java b/src/dk/camelot64/kickc/icl/StatementPhi.java new file mode 100644 index 000000000..1fe4ab2f8 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/StatementPhi.java @@ -0,0 +1,49 @@ +package dk.camelot64.kickc.icl; + +import java.util.ArrayList; +import java.util.List; + +/** + * Single Static Assignment Form Phi Statement. + * Selects appropriate value of a variable based on the actual control flow. + *
+ * Xi := phi(Xj, Xk, ...) + */ +public class StatementPhi implements Statement { + + /** The versioned variable being assigned a value by the statement. */ + private Symbol lValue; + + /** The previous version of the symbol from predeccesor control blocks. */ + private List previousVersions; + + + public StatementPhi(Symbol lValue) { + this.lValue = lValue; + this.previousVersions = new ArrayList<>(); + } + + public LValue getlValue() { + return lValue; + } + + public void addPreviousVersion(Symbol previousVersion) { + previousVersions.add(previousVersion); + } + + public List getPreviousVersions() { + return previousVersions; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append(lValue + " ← " + "phi("); + for (Symbol previousVersion : previousVersions) { + out.append(" "+previousVersion.toString()); + } + out.append(" )"); + return out.toString(); + } + +} diff --git a/src/dk/camelot64/kickc/icl/Symbol.java b/src/dk/camelot64/kickc/icl/Symbol.java index 20b05c94f..2c2c84a3d 100644 --- a/src/dk/camelot64/kickc/icl/Symbol.java +++ b/src/dk/camelot64/kickc/icl/Symbol.java @@ -3,19 +3,42 @@ package dk.camelot64.kickc.icl; /** A Symbol (variable, jump label, etc.) */ public class Symbol implements RValue, LValue { + /** The name of the symbol. */ private String name; + /** The type of the symbol. VAR means tha type is unknown, and has not been inferred yet. */ private SymbolType type; + /** true if the symbol type is infered (not declared) */ + private boolean inferredType; + + /** true if this is an intermediate variable that is created as a part of evaluating an expression. */ private boolean intermediate; - private boolean inferredType; + /** If the symbol is a version of another symbol created during generation of single static assignment form (SSA) this contains the main symbol. */ + private Symbol versionOf; + + /** The number of the next version (if anyone versions this symbol)*/ + private Integer nextVersionNumber; public Symbol(String name, SymbolType type, boolean intermediate) { this.name = name; this.type = type; this.intermediate = intermediate; this.inferredType = false; + this.versionOf = null; + if(!intermediate) { + this.nextVersionNumber = 0; + } + } + + Symbol(Symbol versionOf, int version) { + this.name = versionOf.getName() + "#" + version; + this.type = versionOf.getType(); + this.intermediate = versionOf.isIntermediate(); + this.inferredType = versionOf.isInferredType(); + this.versionOf = versionOf; + this.nextVersionNumber = null; } public String getName() { @@ -35,9 +58,14 @@ public class Symbol implements RValue, LValue { return intermediate; } + /** Get the version number of the next version. (if anyone versions the symbol). */ + int getNextVersionNumber() { + return nextVersionNumber++; + } + @Override public String toString() { - return "("+name + ": " + type.getTypeName() + (inferredType ?"*":"") + ")"; + return "("+type.getTypeName() + (inferredType ?"*":"") + ") "+name; } @Override @@ -45,8 +73,6 @@ public class Symbol implements RValue, LValue { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Symbol symbol = (Symbol) o; - if (intermediate != symbol.intermediate) return false; - if (inferredType != symbol.inferredType) return false; if (!name.equals(symbol.name)) return false; return type == symbol.type; } @@ -54,9 +80,18 @@ public class Symbol implements RValue, LValue { @Override public int hashCode() { int result = name.hashCode(); - result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (intermediate ? 1 : 0); - result = 31 * result + (inferredType ? 1 : 0); return result; } + + public boolean isInferredType() { + return inferredType; + } + + public boolean isVersioned() { + return versionOf != null; + } + + public Symbol getVersionOf() { + return versionOf; + } } diff --git a/src/dk/camelot64/kickc/icl/SymbolManager.java b/src/dk/camelot64/kickc/icl/SymbolManager.java index 67145768c..6c79a90ef 100644 --- a/src/dk/camelot64/kickc/icl/SymbolManager.java +++ b/src/dk/camelot64/kickc/icl/SymbolManager.java @@ -9,7 +9,6 @@ import java.util.Map; public class SymbolManager { private Map symbols; - private int intermediateVarCount = 0; private int intermediateLabelCount = 1; @@ -69,4 +68,10 @@ public class SymbolManager { public void remove(Symbol symbol) { symbols.remove(symbol.getName()); } + + public Symbol createVersion(Symbol symbol) { + Symbol version = new Symbol(symbol, symbol.getNextVersionNumber()); + symbols.put(version.getName(), version); + return version; + } } diff --git a/src/dk/camelot64/kickc/test/main.java b/src/dk/camelot64/kickc/test/main.java index 4aa97f8fc..8ea590a6d 100644 --- a/src/dk/camelot64/kickc/test/main.java +++ b/src/dk/camelot64/kickc/test/main.java @@ -1,11 +1,8 @@ package dk.camelot64.kickc.test; -import dk.camelot64.kickc.icl.ControlFlowGraph; -import dk.camelot64.kickc.icl.PassGenerateControlFlowGraph; +import dk.camelot64.kickc.icl.*; import dk.camelot64.kickc.parser.KickCLexer; import dk.camelot64.kickc.parser.KickCParser; -import dk.camelot64.kickc.icl.PassGenerateStatementSequence; -import dk.camelot64.kickc.icl.PassTypeInference; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -22,14 +19,18 @@ public class main { parser.setBuildParseTree(true); KickCParser.FileContext file = parser.file(); PassGenerateStatementSequence passGenerateStatementSequence = new PassGenerateStatementSequence(); - passGenerateStatementSequence.visit(file); - new PassTypeInference().inferTypes(passGenerateStatementSequence.getSequence(), passGenerateStatementSequence.getSymbols()); - PassGenerateControlFlowGraph passGenerateControlFlowGraph = new PassGenerateControlFlowGraph(passGenerateStatementSequence.getSymbols()); - ControlFlowGraph controlFlowGraph = passGenerateControlFlowGraph.generate(passGenerateStatementSequence.getSequence()); + passGenerateStatementSequence.generate(file); + StatementSequence statementSequence = passGenerateStatementSequence.getSequence(); + SymbolManager symbolManager = passGenerateStatementSequence.getSymbols(); + new PassTypeInference().inferTypes(statementSequence, symbolManager); + PassGenerateControlFlowGraph passGenerateControlFlowGraph = new PassGenerateControlFlowGraph(symbolManager); + ControlFlowGraph controlFlowGraph = passGenerateControlFlowGraph.generate(statementSequence); + PassGenerateSingleStaticAssignmentForm passGenerateSingleStaticAssignmentForm = new PassGenerateSingleStaticAssignmentForm(symbolManager, controlFlowGraph); + passGenerateSingleStaticAssignmentForm.generate(); System.out.println("SYMBOLS"); - System.out.println(passGenerateStatementSequence.getSymbols().toString()); + System.out.println(symbolManager.toString()); System.out.println("PROGRAM"); - System.out.println(passGenerateStatementSequence.getSequence().toString()); + System.out.println(statementSequence.toString()); System.out.println("CONTROL FLOW GRAPH"); System.out.println(controlFlowGraph.toString()); diff --git a/src/dk/camelot64/kickc/test/test.kc b/src/dk/camelot64/kickc/test/test.kc index 2b83b3453..76248d79f 100644 --- a/src/dk/camelot64/kickc/test/test.kc +++ b/src/dk/camelot64/kickc/test/test.kc @@ -2,6 +2,9 @@ byte a = 0; word b = 0; while(a<10) { b=b+a; + if(b>10) { + b = b-10; + } a=a+1; } byte c = -a; \ No newline at end of file