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;
|
||||
}
|
||||
|
||||
public SymbolType getType() {
|
||||
return PassTypeInference.inferType(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(number);
|
||||
return "("+getType().getTypeName()+") "+Integer.toString(number);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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<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();
|
||||
}
|
||||
|
||||
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<RValue> {
|
||||
public SymbolManager getSymbols() {
|
||||
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;
|
||||
if (rValue instanceof Symbol) {
|
||||
Symbol rSymbol = (Symbol) rValue;
|
||||
|
@ -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 ;
|
||||
}
|
||||
|
||||
}
|
||||
|
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.) */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import java.util.Map;
|
||||
public class SymbolManager {
|
||||
|
||||
private Map<String, Symbol> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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;
|
Loading…
Reference in New Issue
Block a user