1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-02 00:41:42 +00:00

Constructing Control Flow Graph.

This commit is contained in:
jespergravgaard 2017-05-07 20:32:30 +02:00
parent a06fe6e989
commit 4eb905b15c
36 changed files with 646 additions and 481 deletions

View File

@ -0,0 +1,7 @@
package dk.camelot64.kickc.icl;
/** SSA form constant value */
public interface Constant extends RValue {
}

View File

@ -1,13 +1,13 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* SSA form constant integer value
*/
public class SSAConstantBool implements SSAConstant {
public class ConstantBool implements Constant {
private Boolean value;
public SSAConstantBool(Boolean value) {
public ConstantBool(Boolean value) {
this.value = value;
}

View File

@ -1,13 +1,13 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* SSA form constant integer value
*/
public class SSAConstantDouble implements SSAConstant {
public class ConstantDouble implements Constant {
private Double number;
public SSAConstantDouble(Double number) {
public ConstantDouble(Double number) {
this.number= number;
}

View File

@ -1,13 +1,13 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* SSA form constant integer value
*/
public class SSAConstantInteger implements SSAConstant {
public class ConstantInteger implements Constant {
private Integer number;
public SSAConstantInteger(Integer number) {
public ConstantInteger(Integer number) {
this.number = number;
}

View File

@ -1,13 +1,13 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* SSA form constant integer value
*/
public class SSAConstantString implements SSAConstant {
public class ConstantString implements Constant {
private String value;
public SSAConstantString(String value) {
public ConstantString(String value) {
this.value = value;
}

View File

@ -0,0 +1,101 @@
package dk.camelot64.kickc.icl;
import java.util.ArrayList;
import java.util.List;
/** A named/labelled sequence of SSA statements connected to other basic blocks.
* The connections defines the control flow of the program. */
public class ControlFlowBlock {
private Symbol label;
private List<ControlFlowBlock> predecessors;
private List<Statement> statements;
private ControlFlowBlock defaultSuccessor;
private ControlFlowBlock conditionalSuccessor;
public ControlFlowBlock(Symbol label) {
this.label = label;
this.statements = new ArrayList<>();
this.predecessors = new ArrayList<>();
this.defaultSuccessor = null;
this.conditionalSuccessor = null;
}
public Symbol getLabel() {
return label;
}
public void addStatement(Statement statement) {
this.statements.add(statement);
}
public void addPredecessor(ControlFlowBlock block) {
this.predecessors.add(block);
}
public void setDefaultSuccessor(ControlFlowBlock defaultSuccessor) {
this.defaultSuccessor = defaultSuccessor;
}
public ControlFlowBlock getDefaultSuccessor() {
return defaultSuccessor;
}
public ControlFlowBlock getConditionalSuccessor() {
return conditionalSuccessor;
}
public List<ControlFlowBlock> getPredecessors() {
return predecessors;
}
public void setConditionalSuccessor(ControlFlowBlock conditionalSuccessor) {
this.conditionalSuccessor = conditionalSuccessor;
}
public void removePredecessor(ControlFlowBlock block) {
predecessors.remove(block);
}
public List<Statement> getStatements() {
return statements;
}
@Override
public String toString() {
StringBuffer out = new StringBuffer();
out.append(label.getName() + ":" + "\n");
out.append(" from:");
for (ControlFlowBlock predecessor : predecessors) {
out.append(predecessor.getLabel().getName()+" ");
}
out.append("\n");
for (Statement statement : statements) {
out.append(" "+statement+"\n");
}
out.append(" to:");
if(defaultSuccessor!=null) {
out.append(defaultSuccessor.getLabel().getName());
}
out.append("\n");
return out.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ControlFlowBlock that = (ControlFlowBlock) o;
return label.equals(that.label);
}
@Override
public int hashCode() {
return label.hashCode();
}
}

View File

@ -0,0 +1,34 @@
package dk.camelot64.kickc.icl;
import java.util.Map;
/** The control flow graph of the program.
* The control flow graph is a set of connected basic blocks. */
public class ControlFlowGraph {
private Map<Symbol, ControlFlowBlock> blocks;
private ControlFlowBlock firstBlock;
public ControlFlowGraph(Map<Symbol, ControlFlowBlock> blocks, ControlFlowBlock firstBlock) {
this.blocks = blocks;
this.firstBlock = firstBlock;
}
public ControlFlowBlock getBlock(Symbol symbol) {
return blocks.get(symbol);
}
public ControlFlowBlock getFirstBlock() {
return firstBlock;
}
@Override
public String toString() {
StringBuffer out = new StringBuffer();
for (ControlFlowBlock block : blocks.values()) {
out.append(block);
}
return out.toString();
}
}

View File

@ -0,0 +1,5 @@
package dk.camelot64.kickc.icl;
/** Assignable value (capable of being on the left part of an assignment)*/
public interface LValue {
}

View File

@ -1,7 +1,7 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/** Parser for converting literal numbers to the corresponding Java Integer/Double */
public class KickCNumberParser {
public class NumberParser {
public static Number parseLiteral(String literal) {
boolean isInt = !literal.contains(".");

View File

@ -1,11 +1,11 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/** SSA Form Operator. The operation performed on the rvalues in an SSA Statement. */
public class SSAOperator {
/** An Operator. The operation performed on the rvalues in a Statement. */
public class Operator {
private String operator;
public SSAOperator(String operator) {
public Operator(String operator) {
this.operator = operator;
}

View File

@ -0,0 +1,91 @@
package dk.camelot64.kickc.icl;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Pass that generates a control flow graph for the program */
public class PassGenerateControlFlowGraph {
public static final String FIRST_BLOCK_NAME = "@0";
private SymbolManager symbolManager;
private Map<Symbol, ControlFlowBlock> blocks;
private ControlFlowBlock firstBlock;
public PassGenerateControlFlowGraph(SymbolManager symbolManager) {
this.symbolManager = symbolManager;
this.blocks = new LinkedHashMap<>();
}
public ControlFlowGraph generate(StatementSequence sequence) {
this.firstBlock = getOrCreateBlock(symbolManager.newNamedJumpLabel(FIRST_BLOCK_NAME));
ControlFlowBlock currentBlock = this.firstBlock;
for (Statement statement : sequence.getStatements()) {
if(statement instanceof StatementJumpTarget) {
StatementJumpTarget statementJumpTarget = (StatementJumpTarget) statement;
ControlFlowBlock nextBlock = getOrCreateBlock(statementJumpTarget.getLabel());
currentBlock.setDefaultSuccessor(nextBlock);
nextBlock.addPredecessor(currentBlock);
currentBlock = nextBlock;
} else if(statement instanceof StatementJump) {
StatementJump statementJump = (StatementJump) statement;
ControlFlowBlock jmpBlock = getOrCreateBlock(statementJump.getDestination());
currentBlock.setDefaultSuccessor(jmpBlock);
jmpBlock.addPredecessor(currentBlock);
ControlFlowBlock nextBlock = getOrCreateBlock(symbolManager.newIntermediateJumpLabel());
currentBlock = nextBlock;
} else if(statement instanceof StatementConditionalJump) {
currentBlock.addStatement(statement);
StatementConditionalJump statementConditionalJump = (StatementConditionalJump) statement;
ControlFlowBlock jmpBlock = getOrCreateBlock(statementConditionalJump.getDestination());
ControlFlowBlock nextBlock = getOrCreateBlock(symbolManager.newIntermediateJumpLabel());
currentBlock.setDefaultSuccessor(nextBlock);
currentBlock.setConditionalSuccessor(jmpBlock);
nextBlock.addPredecessor(currentBlock);
jmpBlock.addPredecessor(currentBlock);
currentBlock = nextBlock;
} else {
currentBlock.addStatement(statement);
}
}
cullEmptyBlocks();
return new ControlFlowGraph(blocks, firstBlock);
}
private void cullEmptyBlocks() {
List<ControlFlowBlock> remove = new ArrayList<>();
for (ControlFlowBlock block : blocks.values()) {
if(block.getStatements().isEmpty()) {
ControlFlowBlock successor = block.getDefaultSuccessor();
for (ControlFlowBlock predecessor : block.getPredecessors()) {
if(predecessor.getDefaultSuccessor().equals(block)) {
predecessor.setDefaultSuccessor(successor);
successor.addPredecessor(predecessor);
}
if(predecessor.getConditionalSuccessor().equals(block)) {
predecessor.setConditionalSuccessor(successor);
successor.addPredecessor(predecessor);
}
}
successor.removePredecessor(block);
remove.add(block);
}
}
for (ControlFlowBlock block : remove) {
blocks.remove(block.getLabel());
symbolManager.remove(block.getLabel());
}
}
private ControlFlowBlock getOrCreateBlock(Symbol label) {
ControlFlowBlock block = blocks.get(label);
if(block==null) {
block = new ControlFlowBlock(label);
blocks.put(block.getLabel(), block);
}
return block;
}
}

View File

@ -0,0 +1,172 @@
package dk.camelot64.kickc.icl;
import dk.camelot64.kickc.parser.KickCBaseVisitor;
import dk.camelot64.kickc.parser.KickCParser;
import org.antlr.v4.runtime.tree.TerminalNode;
/** Generates program SSA form by visiting the ANTLR4 parse tree*/
public class PassGenerateStatementSequence extends KickCBaseVisitor<RValue> {
private SymbolManager symbolManager;
private StatementSequence sequence;
public PassGenerateStatementSequence() {
this.symbolManager = new SymbolManager();
this.sequence = new StatementSequence();
}
@Override
public RValue visitFile(KickCParser.FileContext ctx) {
this.visit(ctx.stmtSeq());
return null;
}
@Override
public RValue visitStmtSeq(KickCParser.StmtSeqContext ctx) {
for(int i=0; i<ctx.getChildCount(); i++) {
this.visit(ctx.stmt(i));
}
return null;
}
@Override
public RValue visitStmtBlock(KickCParser.StmtBlockContext ctx) {
this.visit(ctx.stmtSeq());
return null;
}
@Override
public RValue visitStmtExpr(KickCParser.StmtExprContext ctx) {
this.visit(ctx.expr());
return null;
}
@Override
public RValue visitStmtIfElse(KickCParser.StmtIfElseContext ctx) {
RValue rValue = this.visit(ctx.expr());
Symbol ifJumpLabel = symbolManager.newIntermediateJumpLabel();
Symbol elseJumpLabel = symbolManager.newIntermediateJumpLabel();
Statement ifJmpStmt = new StatementConditionalJump(rValue, ifJumpLabel);
sequence.addStatement(ifJmpStmt);
Statement elseJmpStmt = new StatementJump(elseJumpLabel);
sequence.addStatement(elseJmpStmt);
StatementJumpTarget ifJumpTarget = new StatementJumpTarget(ifJumpLabel);
sequence.addStatement(ifJumpTarget);
this.visit(ctx.stmt(0));
KickCParser.StmtContext elseStmt = ctx.stmt(1);
if(elseStmt!=null) {
Symbol endJumpLabel = symbolManager.newIntermediateJumpLabel();
Statement endJmpStmt = new StatementJump(endJumpLabel);
sequence.addStatement(endJmpStmt);
StatementJumpTarget elseJumpTarget = new StatementJumpTarget(elseJumpLabel);
sequence.addStatement(elseJumpTarget);
this.visit(elseStmt);
StatementJumpTarget endJumpTarget = new StatementJumpTarget(endJumpLabel);
sequence.addStatement(endJumpTarget);
} else {
StatementJumpTarget elseJumpTarget = new StatementJumpTarget(elseJumpLabel);
sequence.addStatement(elseJumpTarget);
}
return null;
}
@Override
public RValue visitStmtWhile(KickCParser.StmtWhileContext ctx) {
Symbol beginJumpLabel = symbolManager.newIntermediateJumpLabel();
Symbol doJumpLabel = symbolManager.newIntermediateJumpLabel();
Symbol endJumpLabel = symbolManager.newIntermediateJumpLabel();
StatementJumpTarget beginJumpTarget = new StatementJumpTarget(beginJumpLabel);
sequence.addStatement(beginJumpTarget);
RValue rValue = this.visit(ctx.expr());
Statement doJmpStmt = new StatementConditionalJump(rValue, doJumpLabel);
sequence.addStatement(doJmpStmt);
Statement endJmpStmt = new StatementJump(endJumpLabel);
sequence.addStatement(endJmpStmt);
StatementJumpTarget doJumpTarget = new StatementJumpTarget(doJumpLabel);
sequence.addStatement(doJumpTarget);
this.visit(ctx.stmt());
Statement beginJmpStmt = new StatementJump(beginJumpLabel);
sequence.addStatement(beginJmpStmt);
StatementJumpTarget endJumpTarget = new StatementJumpTarget(endJumpLabel);
sequence.addStatement(endJumpTarget);
return null;
}
@Override
public RValue visitStmtAssignment(KickCParser.StmtAssignmentContext ctx) {
if(ctx.TYPE()!=null) {
symbolManager.newVariableDeclaration(ctx.NAME().getText(), ctx.TYPE().getText());
}
if(ctx.expr()!=null) {
RValue rValue = this.visit(ctx.expr());
Symbol variable = symbolManager.newVariableAssignment(ctx.NAME().getText());
Statement stmt = new StatementAssignment(variable, rValue);
sequence.addStatement(stmt);
}
return null;
}
@Override
public RValue visitExprNumber(KickCParser.ExprNumberContext ctx) {
Number number = NumberParser.parseLiteral(ctx.getText());
if(number instanceof Integer) {
return new ConstantInteger((Integer) number);
} else {
return new ConstantDouble((Double) number);
}
}
@Override
public RValue visitExprString(KickCParser.ExprStringContext ctx) {
return new ConstantString(ctx.getText());
}
@Override
public RValue visitExprBool(KickCParser.ExprBoolContext ctx) {
String bool = ctx.getText();
return new ConstantBool(Boolean.valueOf(bool));
}
@Override
public RValue visitExprBinary(KickCParser.ExprBinaryContext ctx) {
RValue left = this.visit(ctx.expr(0));
RValue right = this.visit(ctx.expr(1));
String op = ((TerminalNode)ctx.getChild(1)).getSymbol().getText();
Operator operator = new Operator(op);
Symbol tmpVar = symbolManager.newIntermediateAssignment();
Statement stmt = new StatementAssignment(tmpVar, left, operator, right);
sequence.addStatement(stmt);
return tmpVar;
}
@Override
public RValue visitExprUnary(KickCParser.ExprUnaryContext ctx) {
RValue child = this.visit(ctx.expr());
String op = ((TerminalNode)ctx.getChild(0)).getSymbol().getText();
Operator operator = new Operator(op);
Symbol tmpVar = symbolManager.newIntermediateAssignment();
Statement stmt = new StatementAssignment(tmpVar, operator, child);
sequence.addStatement(stmt);
return tmpVar;
}
@Override
public RValue visitExprPar(KickCParser.ExprParContext ctx) {
return this.visit(ctx.expr());
}
@Override
public RValue visitExprId(KickCParser.ExprIdContext ctx) {
return symbolManager.newVariableUsage(ctx.NAME().getText());
}
public StatementSequence getSequence() {
return sequence;
}
public SymbolManager getSymbols() {
return this.symbolManager;
}
}

View File

@ -1,21 +1,21 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* Pass through the SSA statements inferring types of unresolved variables.
*/
public class PassTypeInference {
public void inferTypes(SSASequence sequence, SymbolManager symbols) {
for (SSAStatement statement : sequence.getStatements()) {
if (statement instanceof SSAStatementAssignment) {
SSAStatementAssignment assignment = (SSAStatementAssignment) statement;
public void inferTypes(StatementSequence sequence, SymbolManager symbols) {
for (Statement statement : sequence.getStatements()) {
if (statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
Symbol symbol = (Symbol) assignment.getlValue();
if (SymbolType.VAR.equals(symbol.getType())) {
// Unresolved symbol - perform inference
SSAOperator operator = assignment.getOperator();
Operator operator = assignment.getOperator();
if (operator == null || assignment.getRValue1() == null) {
// Copy operation or Unary operation
SSARValue rValue = assignment.getRValue2();
RValue rValue = assignment.getRValue2();
SymbolType type = inferType(rValue);
symbol.setInferredType(type);
} else {
@ -30,7 +30,7 @@ public class PassTypeInference {
}
}
private SymbolType inferType(SymbolType type1, SSAOperator operator, SymbolType type2) {
private SymbolType inferType(SymbolType type1, Operator operator, SymbolType type2) {
String op = operator.getOperator();
switch (op) {
case "==":
@ -61,21 +61,21 @@ public class PassTypeInference {
}
}
private SymbolType inferType(SSARValue rValue) {
private SymbolType inferType(RValue rValue) {
SymbolType type = SymbolType.VAR;
if (rValue instanceof Symbol) {
Symbol rSymbol = (Symbol) rValue;
type = rSymbol.getType();
} else if (rValue instanceof SSAConstantInteger) {
SSAConstantInteger rInt = (SSAConstantInteger) rValue;
} else if (rValue instanceof ConstantInteger) {
ConstantInteger rInt = (ConstantInteger) rValue;
if (rInt.getNumber() < 256) {
type = SymbolType.BYTE;
} else {
type = SymbolType.WORD;
}
} else if (rValue instanceof SSAConstantString) {
} else if (rValue instanceof ConstantString) {
type = SymbolType.STRING;
} else if (rValue instanceof SSAConstantBool) {
} else if (rValue instanceof ConstantBool) {
type = SymbolType.BOOLEAN;
}
return type;

View File

@ -0,0 +1,5 @@
package dk.camelot64.kickc.icl;
/** A value usable as part of a calculation (ib the right side of an assignment)*/
public interface RValue {
}

View File

@ -1,10 +1,10 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* Single Static Assignment Form Statement.
* Intermediate form used for compiler optimization.
*/
public interface SSAStatement extends SSAFragment {
public interface Statement {
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* Single Static Assignment Form Statement.
@ -8,56 +8,56 @@ package dk.camelot64.kickc.ssa;
* <br>
* <i> lValue := rValue1 &lt;operator&gt; rValue2 </i>
*/
public class SSAStatementAssignment implements SSAStatement {
public class StatementAssignment implements Statement {
/** The variable being assigned a value by the statement. */
private SSALValue lValue;
private LValue lValue;
private SSARValue rValue1;
private SSAOperator operator;
private SSARValue rValue2;
private RValue rValue1;
private Operator operator;
private RValue rValue2;
public SSAStatementAssignment(SSALValue lValue, SSARValue rValue2) {
public StatementAssignment(LValue lValue, RValue rValue2) {
this.lValue = lValue;
this.rValue1 = null;
this.operator = null;
this.rValue2 = rValue2;
}
public SSAStatementAssignment(SSALValue lValue, SSARValue rValue1, SSAOperator operator, SSARValue rValue2) {
public StatementAssignment(LValue lValue, RValue rValue1, Operator operator, RValue rValue2) {
this.lValue = lValue;
this.rValue1 = rValue1;
this.operator = operator;
this.rValue2 = rValue2;
}
public SSAStatementAssignment(SSALValue lValue, SSAOperator operator, SSARValue rValue2) {
public StatementAssignment(LValue lValue, Operator operator, RValue rValue2) {
this.lValue = lValue;
this.rValue1 = null;
this.operator = operator;
this.rValue2 = rValue2;
}
public SSALValue getlValue() {
public LValue getlValue() {
return lValue;
}
public SSARValue getRValue1() {
public RValue getRValue1() {
return rValue1;
}
public SSAOperator getOperator() {
public Operator getOperator() {
return operator;
}
public SSARValue getRValue2() {
public RValue getRValue2() {
return rValue2;
}
@Override
public String toString() {
return
" "+
lValue + "" +
(rValue1==null?"":rValue1+" ") +
(operator==null?"":operator+" ") +

View File

@ -0,0 +1,31 @@
package dk.camelot64.kickc.icl;
/**
* Intermediate Compiler Form Statement with a conditional jump.
* Intermediate form used for compiler optimization.
* <br>
* <i> if ( Y<sub>j</sub> ) goto XX </i>
*/
public class StatementConditionalJump implements Statement {
private RValue condition;
private Symbol destination;
public StatementConditionalJump(RValue condition, Symbol destination) {
this.condition = condition;
this.destination = destination;
}
public RValue getCondition() {
return condition;
}
public Symbol getDestination() {
return destination;
}
@Override
public String toString() {
return "if("+condition+") goto "+destination.getName();
}
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/**
* Single Static Assignment Form Statement unconditional jump.
@ -6,20 +6,20 @@ package dk.camelot64.kickc.ssa;
* <br>
* <i> goto XX </i>
*/
public class SSAStatementJump implements SSAStatement {
public class StatementJump implements Statement {
private SSAJumpLabel destination;
private Symbol destination;
public SSAStatementJump(SSAJumpLabel destination) {
public StatementJump(Symbol destination) {
this.destination = destination;
}
public SSAJumpLabel getDestination() {
public Symbol getDestination() {
return destination;
}
@Override
public String toString() {
return " "+"goto "+destination;
return "goto "+destination.getName();
}
}

View File

@ -0,0 +1,24 @@
package dk.camelot64.kickc.icl;
/**
* Single Static Assignment Form Statement Jump target.
*/
public class StatementJumpTarget implements Statement {
private Symbol label;
public StatementJumpTarget(Symbol label) {
this.label = label;
}
public Symbol getLabel() {
return label;
}
@Override
public String toString() {
return label+":";
}
}

View File

@ -0,0 +1,34 @@
package dk.camelot64.kickc.icl;
import java.util.ArrayList;
import java.util.List;
/** A sequence of Statements */
public class StatementSequence {
private List<Statement> statements;
public StatementSequence() {
this.statements = new ArrayList<>();
}
public void addStatement(Statement statement) {
this.statements.add(statement);
}
@Override
public String toString() {
StringBuffer out = new StringBuffer();
for (Statement statement : statements) {
if(!(statement instanceof StatementJumpTarget)) {
out.append(" ");
}
out.append(statement.toString()+"\n");
}
return out.toString();
}
public List<Statement> getStatements() {
return statements;
}
}

View File

@ -0,0 +1,62 @@
package dk.camelot64.kickc.icl;
/** A Symbol (variable, jump label, etc.) */
public class Symbol implements RValue, LValue {
private String name;
private SymbolType type;
private boolean intermediate;
private boolean inferredType;
public Symbol(String name, SymbolType type, boolean intermediate) {
this.name = name;
this.type = type;
this.intermediate = intermediate;
this.inferredType = false;
}
public String getName() {
return name;
}
public SymbolType getType() {
return type;
}
public void setInferredType(SymbolType type) {
this.type = type;
this.inferredType = true;
}
public boolean isIntermediate() {
return intermediate;
}
@Override
public String toString() {
return "("+name + ": " + type.getTypeName() + (inferredType ?"*":"") + ")";
}
@Override
public boolean equals(Object o) {
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;
}
@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;
}
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
import java.util.LinkedHashMap;
import java.util.Map;
@ -11,7 +11,7 @@ public class SymbolManager {
private Map<String, Symbol> symbols;
private int intermediateVarCount = 0;
private int intermediateLabelCount = 0;
private int intermediateLabelCount = 1;
public SymbolManager() {
this.symbols = new LinkedHashMap<>();
@ -45,15 +45,15 @@ public class SymbolManager {
return symbol;
}
public SSAJumpLabel newNamedJumpLabel(String name) {
public Symbol newNamedJumpLabel(String name) {
Symbol symbol = addSymbol(name, SymbolType.LABEL, false);
return new SSAJumpLabel(name);
return symbol;
}
public SSAJumpLabel newIntermediateJumpLabel() {
public Symbol newIntermediateJumpLabel() {
String name = "@"+ intermediateLabelCount++;
addSymbol(name, SymbolType.LABEL, true);
return new SSAJumpLabel(name);
Symbol symbol = addSymbol(name, SymbolType.LABEL, true);
return symbol;
}
@Override
@ -65,4 +65,8 @@ public class SymbolManager {
}
return out.toString();
}
public void remove(Symbol symbol) {
symbols.remove(symbol.getName());
}
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.ssa;
package dk.camelot64.kickc.icl;
/** Symbol Types */
public enum SymbolType {

View File

@ -1,172 +0,0 @@
package dk.camelot64.kickc.ssa;
import dk.camelot64.kickc.parser.KickCBaseVisitor;
import dk.camelot64.kickc.parser.KickCParser;
import org.antlr.v4.runtime.tree.TerminalNode;
/** Generates program SSA form by visiting the ANTLR4 parse tree*/
public class GenerateSSA extends KickCBaseVisitor<SSARValue> {
private SymbolManager symbolManager;
private SSASequence sequence;
public GenerateSSA() {
this.symbolManager = new SymbolManager();
this.sequence = new SSASequence();
}
@Override
public SSARValue visitFile(KickCParser.FileContext ctx) {
this.visit(ctx.stmtSeq());
return null;
}
@Override
public SSARValue visitStmtSeq(KickCParser.StmtSeqContext ctx) {
for(int i=0; i<ctx.getChildCount(); i++) {
this.visit(ctx.stmt(i));
}
return null;
}
@Override
public SSARValue visitStmtBlock(KickCParser.StmtBlockContext ctx) {
this.visit(ctx.stmtSeq());
return null;
}
@Override
public SSARValue visitStmtExpr(KickCParser.StmtExprContext ctx) {
this.visit(ctx.expr());
return null;
}
@Override
public SSARValue visitStmtIfElse(KickCParser.StmtIfElseContext ctx) {
SSARValue rValue = this.visit(ctx.expr());
SSAJumpLabel ifJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAJumpLabel elseJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAStatement ifJmpStmt = new SSAStatementConditionalJump(rValue, ifJumpLabel);
sequence.addStatement(ifJmpStmt);
SSAStatement elseJmpStmt = new SSAStatementJump(elseJumpLabel);
sequence.addStatement(elseJmpStmt);
SSAStatementJumpTarget ifJumpTarget = new SSAStatementJumpTarget(ifJumpLabel);
sequence.addStatement(ifJumpTarget);
this.visit(ctx.stmt(0));
KickCParser.StmtContext elseStmt = ctx.stmt(1);
if(elseStmt!=null) {
SSAJumpLabel endJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAStatement endJmpStmt = new SSAStatementJump(endJumpLabel);
sequence.addStatement(endJmpStmt);
SSAStatementJumpTarget elseJumpTarget = new SSAStatementJumpTarget(elseJumpLabel);
sequence.addStatement(elseJumpTarget);
this.visit(elseStmt);
SSAStatementJumpTarget endJumpTarget = new SSAStatementJumpTarget(endJumpLabel);
sequence.addStatement(endJumpTarget);
} else {
SSAStatementJumpTarget elseJumpTarget = new SSAStatementJumpTarget(elseJumpLabel);
sequence.addStatement(elseJumpTarget);
}
return null;
}
@Override
public SSARValue visitStmtWhile(KickCParser.StmtWhileContext ctx) {
SSAJumpLabel beginJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAJumpLabel doJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAJumpLabel endJumpLabel = symbolManager.newIntermediateJumpLabel();
SSAStatementJumpTarget beginJumpTarget = new SSAStatementJumpTarget(beginJumpLabel);
sequence.addStatement(beginJumpTarget);
SSARValue rValue = this.visit(ctx.expr());
SSAStatement doJmpStmt = new SSAStatementConditionalJump(rValue, doJumpLabel);
sequence.addStatement(doJmpStmt);
SSAStatement endJmpStmt = new SSAStatementJump(endJumpLabel);
sequence.addStatement(endJmpStmt);
SSAStatementJumpTarget doJumpTarget = new SSAStatementJumpTarget(doJumpLabel);
sequence.addStatement(doJumpTarget);
this.visit(ctx.stmt());
SSAStatement beginJmpStmt = new SSAStatementJump(beginJumpLabel);
sequence.addStatement(beginJmpStmt);
SSAStatementJumpTarget endJumpTarget = new SSAStatementJumpTarget(endJumpLabel);
sequence.addStatement(endJumpTarget);
return null;
}
@Override
public SSARValue visitStmtAssignment(KickCParser.StmtAssignmentContext ctx) {
if(ctx.TYPE()!=null) {
symbolManager.newVariableDeclaration(ctx.NAME().getText(), ctx.TYPE().getText());
}
if(ctx.expr()!=null) {
SSARValue rValue = this.visit(ctx.expr());
Symbol variable = symbolManager.newVariableAssignment(ctx.NAME().getText());
SSAStatement stmt = new SSAStatementAssignment(variable, rValue);
sequence.addStatement(stmt);
}
return null;
}
@Override
public SSARValue visitExprNumber(KickCParser.ExprNumberContext ctx) {
Number number = KickCNumberParser.parseLiteral(ctx.getText());
if(number instanceof Integer) {
return new SSAConstantInteger((Integer) number);
} else {
return new SSAConstantDouble((Double) number);
}
}
@Override
public SSARValue visitExprString(KickCParser.ExprStringContext ctx) {
return new SSAConstantString(ctx.getText());
}
@Override
public SSARValue visitExprBool(KickCParser.ExprBoolContext ctx) {
String bool = ctx.getText();
return new SSAConstantBool(Boolean.valueOf(bool));
}
@Override
public SSARValue visitExprBinary(KickCParser.ExprBinaryContext ctx) {
SSARValue left = this.visit(ctx.expr(0));
SSARValue right = this.visit(ctx.expr(1));
String op = ((TerminalNode)ctx.getChild(1)).getSymbol().getText();
SSAOperator operator = new SSAOperator(op);
Symbol tmpVar = symbolManager.newIntermediateAssignment();
SSAStatement stmt = new SSAStatementAssignment(tmpVar, left, operator, right);
sequence.addStatement(stmt);
return tmpVar;
}
@Override
public SSARValue visitExprUnary(KickCParser.ExprUnaryContext ctx) {
SSARValue child = this.visit(ctx.expr());
String op = ((TerminalNode)ctx.getChild(0)).getSymbol().getText();
SSAOperator operator = new SSAOperator(op);
Symbol tmpVar = symbolManager.newIntermediateAssignment();
SSAStatement stmt = new SSAStatementAssignment(tmpVar, operator, child);
sequence.addStatement(stmt);
return tmpVar;
}
@Override
public SSARValue visitExprPar(KickCParser.ExprParContext ctx) {
return this.visit(ctx.expr());
}
@Override
public SSARValue visitExprId(KickCParser.ExprIdContext ctx) {
return symbolManager.newVariableUsage(ctx.NAME().getText());
}
public SSASequence getSequence() {
return sequence;
}
public SymbolManager getSymbols() {
return this.symbolManager;
}
}

View File

@ -1,25 +0,0 @@
package dk.camelot64.kickc.ssa;
import java.util.ArrayList;
import java.util.List;
/** A sequence of SSA statements */
public class SSABasicBlock {
List<SSAStatement> statements;
public SSABasicBlock() {
this.statements = new ArrayList<>();
}
public void addStatement(SSAStatement statement) {
this.statements.add(statement);
}
@Override
public String toString() {
return "SSABasicBlock{" +
"statements=" + statements +
'}';
}
}

View File

@ -1,7 +0,0 @@
package dk.camelot64.kickc.ssa;
/** SSA form constant value */
public interface SSAConstant extends SSARValue, SSAFragment {
}

View File

@ -1,7 +0,0 @@
package dk.camelot64.kickc.ssa;
/**
* Any SSA Form fragment resulting from translating a part of the parse tree
*/
public interface SSAFragment {
}

View File

@ -1,16 +0,0 @@
package dk.camelot64.kickc.ssa;
/** A label representing a target of a jump in SSA code */
public class SSAJumpLabel {
private String name;
public SSAJumpLabel(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@ -1,5 +0,0 @@
package dk.camelot64.kickc.ssa;
/** Left value in SSA Form */
public interface SSALValue {
}

View File

@ -1,5 +0,0 @@
package dk.camelot64.kickc.ssa;
/** Right value in SSA FORM */
public interface SSARValue {
}

View File

@ -1,33 +0,0 @@
package dk.camelot64.kickc.ssa;
import java.util.ArrayList;
import java.util.List;
/**
* A sequence of SSA Statements
*/
public class SSASequence {
List<SSAStatement> statements;
public SSASequence() {
this.statements = new ArrayList<>();
}
public void addStatement(SSAStatement statement) {
this.statements.add(statement);
}
@Override
public String toString() {
StringBuffer out = new StringBuffer();
for (SSAStatement statement : statements) {
out.append(statement.toString()+"\n");
}
return out.toString();
}
public List<SSAStatement> getStatements() {
return statements;
}
}

View File

@ -1,31 +0,0 @@
package dk.camelot64.kickc.ssa;
/**
* Single Static Assignment Form Statement with a conditional jump.
* Intermediate form used for compiler optimization.
* <br>
* <i> if ( Y<sub>j</sub> ) goto XX </i>
*/
public class SSAStatementConditionalJump implements SSAStatement {
private SSARValue condition;
private SSAJumpLabel destination;
public SSAStatementConditionalJump(SSARValue condition, SSAJumpLabel destination) {
this.condition = condition;
this.destination = destination;
}
public SSARValue getCondition() {
return condition;
}
public SSAJumpLabel getDestination() {
return destination;
}
@Override
public String toString() {
return " "+"if("+condition+") goto "+destination;
}
}

View File

@ -1,24 +0,0 @@
package dk.camelot64.kickc.ssa;
/**
* Single Static Assignment Form Statement Jump target.
*/
public class SSAStatementJumpTarget implements SSAStatement {
private SSAJumpLabel label;
public SSAStatementJumpTarget(SSAJumpLabel label) {
this.label = label;
}
public SSAJumpLabel getLabel() {
return label;
}
@Override
public String toString() {
return label+":";
}
}

View File

@ -1,40 +0,0 @@
package dk.camelot64.kickc.ssa;
/** An SSA form Variable. SSA form variables come in different flavors:
* <ul>
* <li>Each potential modification of a language variable becomes a separate versioned SSA variable.</li>
* <li>Expressions are broken into separate SSA statements, each defining a new temporary/intermediate variable.</li>
* </ul>
*
* Named variables are initially created without serials. These are first added after the basic control blocks have been defined.
*
* */
public class SSAVariable implements SSARValue, SSALValue, SSAFragment {
private String name;
private Integer serial;
public SSAVariable(String name) {
this.name = name;
this.serial = null;
}
public SSAVariable(String name, int serial) {
this.name = name;
this.serial = serial;
}
public String getName() {
return name;
}
public int getSerial() {
return serial;
}
@Override
public String toString() {
return name + (serial==null?"":"_"+serial);
}
}

View File

@ -1,43 +0,0 @@
package dk.camelot64.kickc.ssa;
/** A Symbol (variable, jump label, etc.) */
public class Symbol implements SSARValue, SSALValue, SSAFragment {
private String name;
private SymbolType type;
private boolean intermediate;
private boolean inferredType;
public Symbol(String name, SymbolType type, boolean intermediate) {
this.name = name;
this.type = type;
this.intermediate = intermediate;
this.inferredType = false;
}
public String getName() {
return name;
}
public SymbolType getType() {
return type;
}
public void setInferredType(SymbolType type) {
this.type = type;
this.inferredType = true;
}
public boolean isIntermediate() {
return intermediate;
}
@Override
public String toString() {
return "("+name + (intermediate?"*":"") + ": " + type.getTypeName() + (inferredType ?"*":"") + ")";
}
}

View File

@ -1,10 +1,11 @@
package dk.camelot64.kickc.test;
import dk.camelot64.kickc.icl.ControlFlowGraph;
import dk.camelot64.kickc.icl.PassGenerateControlFlowGraph;
import dk.camelot64.kickc.parser.KickCLexer;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.ssa.GenerateSSA;
import dk.camelot64.kickc.ssa.PassTypeInference;
import dk.camelot64.kickc.ssa.SSASequence;
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;
@ -20,16 +21,18 @@ public class main {
KickCParser parser = new KickCParser(new CommonTokenStream(lexer));
parser.setBuildParseTree(true);
KickCParser.FileContext file = parser.file();
GenerateSSA ev = new GenerateSSA();
ev.visit(file);
PassTypeInference passTypeInference = new PassTypeInference();
passTypeInference.inferTypes(ev.getSequence(), ev.getSymbols());
System.out.println("PROGRAM");
System.out.println(ev.getSequence().toString());
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());
System.out.println("SYMBOLS");
System.out.println(ev.getSymbols().toString());
System.out.println(passGenerateStatementSequence.getSymbols().toString());
System.out.println("PROGRAM");
System.out.println(passGenerateStatementSequence.getSequence().toString());
System.out.println("CONTROL FLOW GRAPH");
System.out.println(controlFlowGraph.toString());
}
}