1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-18 08:30:18 +00:00

SSA with phi function almost complete

This commit is contained in:
jespergravgaard 2017-05-08 18:08:07 +02:00
parent 4eb905b15c
commit 807198113b
12 changed files with 254 additions and 23 deletions

View File

@ -15,8 +15,12 @@ public class ConstantInteger implements Constant {
return number; return number;
} }
public SymbolType getType() {
return PassTypeInference.inferType(this);
}
@Override @Override
public String toString() { public String toString() {
return Integer.toString(number); return "("+getType().getTypeName()+") "+Integer.toString(number);
} }
} }

View File

@ -65,13 +65,17 @@ public class ControlFlowBlock {
return statements; return statements;
} }
public void addPhiStatement(Symbol newVersion) {
statements.add(0, new StatementPhi(newVersion));
}
@Override @Override
public String toString() { public String toString() {
StringBuffer out = new StringBuffer(); StringBuffer out = new StringBuffer();
out.append(label.getName() + ":" + "\n"); out.append(label.getName() + ":" );
out.append(" from:"); out.append(" from");
for (ControlFlowBlock predecessor : predecessors) { for (ControlFlowBlock predecessor : predecessors) {
out.append(predecessor.getLabel().getName()+" "); out.append(" "+predecessor.getLabel().getName());
} }
out.append("\n"); out.append("\n");
for (Statement statement : statements) { for (Statement statement : statements) {

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc.icl; package dk.camelot64.kickc.icl;
import java.util.Collection;
import java.util.Map; import java.util.Map;
/** The control flow graph of the program. /** The control flow graph of the program.
@ -31,4 +32,8 @@ public class ControlFlowGraph {
} }
return out.toString(); return out.toString();
} }
public Collection<ControlFlowBlock> getAllBlocks() {
return blocks.values();
}
} }

View File

@ -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.
* <p>First versions all variable assignments, then versions all variable usages and introduces necessary Phi-functions,
* <p>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<Symbol, Symbol> blockVersions = new HashMap<>();
// New phi functions introduced in the block to create versions of variables.
Map<Symbol, Symbol> 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<Symbol, Symbol> blockVersions, Map<Symbol, Symbol> 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;
}
}

View File

@ -15,6 +15,10 @@ public class PassGenerateStatementSequence extends KickCBaseVisitor<RValue> {
this.sequence = new StatementSequence(); this.sequence = new StatementSequence();
} }
public void generate(KickCParser.FileContext file) {
this.visit(file);
}
@Override @Override
public RValue visitFile(KickCParser.FileContext ctx) { public RValue visitFile(KickCParser.FileContext ctx) {
this.visit(ctx.stmtSeq()); this.visit(ctx.stmtSeq());
@ -169,4 +173,5 @@ public class PassGenerateStatementSequence extends KickCBaseVisitor<RValue> {
public SymbolManager getSymbols() { public SymbolManager getSymbols() {
return this.symbolManager; return this.symbolManager;
} }
} }

View File

@ -61,7 +61,7 @@ public class PassTypeInference {
} }
} }
private SymbolType inferType(RValue rValue) { public static SymbolType inferType(RValue rValue) {
SymbolType type = SymbolType.VAR; SymbolType type = SymbolType.VAR;
if (rValue instanceof Symbol) { if (rValue instanceof Symbol) {
Symbol rSymbol = (Symbol) rValue; Symbol rSymbol = (Symbol) rValue;

View File

@ -42,10 +42,18 @@ public class StatementAssignment implements Statement {
return lValue; return lValue;
} }
public void setLValue(LValue lValue) {
this.lValue = lValue;
}
public RValue getRValue1() { public RValue getRValue1() {
return rValue1; return rValue1;
} }
public void setRValue1(RValue rValue1) {
this.rValue1 = rValue1;
}
public Operator getOperator() { public Operator getOperator() {
return operator; return operator;
} }
@ -54,6 +62,10 @@ public class StatementAssignment implements Statement {
return rValue2; return rValue2;
} }
public void setRValue2(RValue rValue2) {
this.rValue2 = rValue2;
}
@Override @Override
public String toString() { public String toString() {
return return
@ -63,4 +75,5 @@ public class StatementAssignment implements Statement {
(operator==null?"":operator+" ") + (operator==null?"":operator+" ") +
rValue2 ; rValue2 ;
} }
} }

View File

@ -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.
* <br>
* <i> X<sub>i</sub> := phi(X<sub>j</sub>, X<sub>k</sub>, ...) </i>
*/
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<Symbol> 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<Symbol> 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();
}
}

View File

@ -3,19 +3,42 @@ package dk.camelot64.kickc.icl;
/** A Symbol (variable, jump label, etc.) */ /** A Symbol (variable, jump label, etc.) */
public class Symbol implements RValue, LValue { public class Symbol implements RValue, LValue {
/** The name of the symbol. */
private String name; private String name;
/** The type of the symbol. VAR means tha type is unknown, and has not been inferred yet. */
private SymbolType type; 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 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) { public Symbol(String name, SymbolType type, boolean intermediate) {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.intermediate = intermediate; this.intermediate = intermediate;
this.inferredType = false; 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() { public String getName() {
@ -35,9 +58,14 @@ public class Symbol implements RValue, LValue {
return intermediate; return intermediate;
} }
/** Get the version number of the next version. (if anyone versions the symbol). */
int getNextVersionNumber() {
return nextVersionNumber++;
}
@Override @Override
public String toString() { public String toString() {
return "("+name + ": " + type.getTypeName() + (inferredType ?"*":"") + ")"; return "("+type.getTypeName() + (inferredType ?"*":"") + ") "+name;
} }
@Override @Override
@ -45,8 +73,6 @@ public class Symbol implements RValue, LValue {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Symbol symbol = (Symbol) o; Symbol symbol = (Symbol) o;
if (intermediate != symbol.intermediate) return false;
if (inferredType != symbol.inferredType) return false;
if (!name.equals(symbol.name)) return false; if (!name.equals(symbol.name)) return false;
return type == symbol.type; return type == symbol.type;
} }
@ -54,9 +80,18 @@ public class Symbol implements RValue, LValue {
@Override @Override
public int hashCode() { public int hashCode() {
int result = name.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; return result;
} }
public boolean isInferredType() {
return inferredType;
}
public boolean isVersioned() {
return versionOf != null;
}
public Symbol getVersionOf() {
return versionOf;
}
} }

View File

@ -9,7 +9,6 @@ import java.util.Map;
public class SymbolManager { public class SymbolManager {
private Map<String, Symbol> symbols; private Map<String, Symbol> symbols;
private int intermediateVarCount = 0; private int intermediateVarCount = 0;
private int intermediateLabelCount = 1; private int intermediateLabelCount = 1;
@ -69,4 +68,10 @@ public class SymbolManager {
public void remove(Symbol symbol) { public void remove(Symbol symbol) {
symbols.remove(symbol.getName()); symbols.remove(symbol.getName());
} }
public Symbol createVersion(Symbol symbol) {
Symbol version = new Symbol(symbol, symbol.getNextVersionNumber());
symbols.put(version.getName(), version);
return version;
}
} }

View File

@ -1,11 +1,8 @@
package dk.camelot64.kickc.test; package dk.camelot64.kickc.test;
import dk.camelot64.kickc.icl.ControlFlowGraph; import dk.camelot64.kickc.icl.*;
import dk.camelot64.kickc.icl.PassGenerateControlFlowGraph;
import dk.camelot64.kickc.parser.KickCLexer; import dk.camelot64.kickc.parser.KickCLexer;
import dk.camelot64.kickc.parser.KickCParser; 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.CharStream;
import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.CommonTokenStream;
@ -22,14 +19,18 @@ public class main {
parser.setBuildParseTree(true); parser.setBuildParseTree(true);
KickCParser.FileContext file = parser.file(); KickCParser.FileContext file = parser.file();
PassGenerateStatementSequence passGenerateStatementSequence = new PassGenerateStatementSequence(); PassGenerateStatementSequence passGenerateStatementSequence = new PassGenerateStatementSequence();
passGenerateStatementSequence.visit(file); passGenerateStatementSequence.generate(file);
new PassTypeInference().inferTypes(passGenerateStatementSequence.getSequence(), passGenerateStatementSequence.getSymbols()); StatementSequence statementSequence = passGenerateStatementSequence.getSequence();
PassGenerateControlFlowGraph passGenerateControlFlowGraph = new PassGenerateControlFlowGraph(passGenerateStatementSequence.getSymbols()); SymbolManager symbolManager = passGenerateStatementSequence.getSymbols();
ControlFlowGraph controlFlowGraph = passGenerateControlFlowGraph.generate(passGenerateStatementSequence.getSequence()); 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("SYMBOLS");
System.out.println(passGenerateStatementSequence.getSymbols().toString()); System.out.println(symbolManager.toString());
System.out.println("PROGRAM"); System.out.println("PROGRAM");
System.out.println(passGenerateStatementSequence.getSequence().toString()); System.out.println(statementSequence.toString());
System.out.println("CONTROL FLOW GRAPH"); System.out.println("CONTROL FLOW GRAPH");
System.out.println(controlFlowGraph.toString()); System.out.println(controlFlowGraph.toString());

View File

@ -2,6 +2,9 @@ byte a = 0;
word b = 0; word b = 0;
while(a<10) { while(a<10) {
b=b+a; b=b+a;
if(b>10) {
b = b-10;
}
a=a+1; a=a+1;
} }
byte c = -a; byte c = -a;