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:
parent
4eb905b15c
commit
807198113b
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
49
src/dk/camelot64/kickc/icl/StatementPhi.java
Normal file
49
src/dk/camelot64/kickc/icl/StatementPhi.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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;
|
Loading…
Reference in New Issue
Block a user