From 3b2cc5cde1126c356bf03dcd86610bc0efc3e9a4 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 11 Jun 2017 23:36:05 +0200 Subject: [PATCH] Refactoring control flow graph to enable proc calls. Still need to handle return values properly. --- .idea/dictionaries/jespergravgaard.xml | 7 + .../camelot64/kickc/icl/ControlFlowBlock.java | 56 +++--- .../camelot64/kickc/icl/ControlFlowGraph.java | 27 ++- .../icl/ControlFlowGraphBaseVisitor.java | 26 ++- .../icl/ControlFlowGraphCopyVisitor.java | 180 ++++++++++++++++++ .../icl/Pass1GenerateControlFlowGraph.java | 30 +-- ...ss1GenerateSingleStaticAssignmentForm.java | 80 +++++--- .../icl/Pass1ProcedureCallParameters.java | 46 +++++ .../icl/Pass1ProcedureCallsReturnValue.java | 38 ++++ .../kickc/icl/Pass1TypeInference.java | 3 + .../kickc/icl/Pass2CullEmptyBlocks.java | 34 ++-- .../kickc/icl/Pass2SsaOptimization.java | 4 +- .../kickc/icl/Pass3CodeGeneration.java | 6 +- src/dk/camelot64/kickc/icl/Scope.java | 1 + .../kickc/icl/StatementCallLValue.java | 24 ++- src/dk/camelot64/kickc/icl/StatementPhi.java | 15 +- src/dk/camelot64/kickc/test/Main.java | 18 +- 17 files changed, 490 insertions(+), 105 deletions(-) create mode 100644 .idea/dictionaries/jespergravgaard.xml create mode 100644 src/dk/camelot64/kickc/icl/ControlFlowGraphCopyVisitor.java create mode 100644 src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java create mode 100644 src/dk/camelot64/kickc/icl/Pass1ProcedureCallsReturnValue.java diff --git a/.idea/dictionaries/jespergravgaard.xml b/.idea/dictionaries/jespergravgaard.xml new file mode 100644 index 000000000..8afc30bf5 --- /dev/null +++ b/.idea/dictionaries/jespergravgaard.xml @@ -0,0 +1,7 @@ + + + + unversioned + + + \ No newline at end of file diff --git a/src/dk/camelot64/kickc/icl/ControlFlowBlock.java b/src/dk/camelot64/kickc/icl/ControlFlowBlock.java index 16e7fc27a..69478ecb1 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowBlock.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowBlock.java @@ -4,23 +4,23 @@ 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. */ + * The connections defines the control flow of the program. + * The block only knows its own successors. To find predecessor blocks access to the entire graph is needed.*/ public class ControlFlowBlock { private Label label; - private List predecessors; - private List statements; - private ControlFlowBlock defaultSuccessor; + private Label defaultSuccessor; - private ControlFlowBlock conditionalSuccessor; + private Label conditionalSuccessor; + + private Label callSuccessor; public ControlFlowBlock(Label label) { this.label = label; this.statements = new ArrayList<>(); - this.predecessors = new ArrayList<>(); this.defaultSuccessor = null; this.conditionalSuccessor = null; } @@ -33,32 +33,28 @@ public class ControlFlowBlock { this.statements.add(statement); } - public void addPredecessor(ControlFlowBlock block) { - this.predecessors.add(block); - } - - public void setDefaultSuccessor(ControlFlowBlock defaultSuccessor) { + public void setDefaultSuccessor(Label defaultSuccessor) { this.defaultSuccessor = defaultSuccessor; } - public ControlFlowBlock getDefaultSuccessor() { + public Label getDefaultSuccessor() { return defaultSuccessor; } - public ControlFlowBlock getConditionalSuccessor() { + public Label getConditionalSuccessor() { return conditionalSuccessor; } - public List getPredecessors() { - return predecessors; - } - - public void setConditionalSuccessor(ControlFlowBlock conditionalSuccessor) { + public void setConditionalSuccessor(Label conditionalSuccessor) { this.conditionalSuccessor = conditionalSuccessor; } - public void removePredecessor(ControlFlowBlock block) { - predecessors.remove(block); + public Label getCallSuccessor() { + return callSuccessor; + } + + public void setCallSuccessor(Label callSuccessor) { + this.callSuccessor = callSuccessor; } public List getStatements() { @@ -71,13 +67,22 @@ public class ControlFlowBlock { @Override public String toString() { + return toString(null); + } + + public String toString(ControlFlowGraph graph) { StringBuffer out = new StringBuffer(); out.append(label.getLocalName() + ":" ); - if(predecessors.size()>0) { - out.append(" from"); - for (ControlFlowBlock predecessor : predecessors) { - out.append(" " + predecessor.getLabel().getLocalName()); + out.append(" from"); + if(graph!=null) { + List predecessors = graph.getPredecessors(this); + if(predecessors.size()>0) { + for (ControlFlowBlock predecessor : predecessors) { + out.append(" " + predecessor.getLabel().getLocalName()); + } } + } else { + out.append(" @UNKNOWN"); } out.append("\n"); for (Statement statement : statements) { @@ -85,7 +90,7 @@ public class ControlFlowBlock { } if(defaultSuccessor!=null) { out.append(" to:"); - out.append(defaultSuccessor.getLabel().getLocalName()); + out.append(defaultSuccessor.getLocalName()); out.append("\n"); } return out.toString(); @@ -112,4 +117,5 @@ public class ControlFlowBlock { } return false; } + } diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java index cf5a69723..fb7d73a94 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java @@ -1,6 +1,8 @@ package dk.camelot64.kickc.icl; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; /** The control flow graph of the program. @@ -28,7 +30,7 @@ public class ControlFlowGraph { public String toString() { StringBuffer out = new StringBuffer(); for (ControlFlowBlock block : blocks.values()) { - out.append(block.toString()); + out.append(block.toString(this)); } return out.toString(); } @@ -59,4 +61,27 @@ public class ControlFlowGraph { return null; } + public ControlFlowBlock getDefaultSuccessor(ControlFlowBlock block) { + if(block.getDefaultSuccessor()!=null) { + return blocks.get(block.getDefaultSuccessor()); + } else { + return null; + } + } + + public List getPredecessors(ControlFlowBlock block) { + ArrayList predecessorBlocks = new ArrayList<>(); + for (ControlFlowBlock other : getAllBlocks()) { + if(block.getLabel().equals(other.getDefaultSuccessor())) { + predecessorBlocks.add(other); + } + if(block.getLabel().equals(other.getConditionalSuccessor())) { + predecessorBlocks.add(other); + } + if(block.getLabel().equals(other.getCallSuccessor())) { + predecessorBlocks.add(other); + } + } + return predecessorBlocks; + } } diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraphBaseVisitor.java b/src/dk/camelot64/kickc/icl/ControlFlowGraphBaseVisitor.java index 21c2bb01d..4555f63c2 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowGraphBaseVisitor.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraphBaseVisitor.java @@ -20,7 +20,7 @@ public class ControlFlowGraphBaseVisitor { return null; } - public T visitStatement(Statement statement) { + public Object visitStatement(Statement statement) { if(statement instanceof StatementAssignment) { return visitAssignment((StatementAssignment) statement); } else if(statement instanceof StatementConditionalJump) { @@ -31,11 +31,31 @@ public class ControlFlowGraphBaseVisitor { return visitJumpTarget((StatementLabel) statement); } else if(statement instanceof StatementPhi) { return visitPhi((StatementPhi) statement); + } else if(statement instanceof StatementCallLValue) { + return visitCallLValue((StatementCallLValue) statement); + } else if(statement instanceof StatementReturn) { + return visitReturn((StatementReturn) statement); + } else if(statement instanceof StatementProcedureBegin) { + return visitProcedureBegin((StatementProcedureBegin) statement); + } else if(statement instanceof StatementProcedureEnd) { + return visitProcedureEnd((StatementProcedureEnd) statement); } else { throw new RuntimeException("Unhandled statement type "+statement); } } + public T visitProcedureBegin(StatementProcedureBegin statement) { + return null; + } + + public T visitProcedureEnd(StatementProcedureEnd statement) { + return null; + } + + public T visitReturn(StatementReturn aReturn) { + return null; + } + public T visitAssignment(StatementAssignment assignment) { return null; } @@ -56,4 +76,8 @@ public class ControlFlowGraphBaseVisitor { return null; } + public T visitCallLValue(StatementCallLValue callLValue) { + return null; + } + } diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraphCopyVisitor.java b/src/dk/camelot64/kickc/icl/ControlFlowGraphCopyVisitor.java new file mode 100644 index 000000000..c9b6ce42d --- /dev/null +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraphCopyVisitor.java @@ -0,0 +1,180 @@ +package dk.camelot64.kickc.icl; + +import java.util.HashMap; +import java.util.List; + +/** + * A visitor that copies a complete control flow graph. contains visitor-methods usable for modifying the copy at any point + */ +public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor { + + /** + * The origGraph graph. + */ + private ControlFlowGraph origGraph; + + /** + * The copied blocks. + */ + private HashMap copyBlockMap; + + /** + * The current block being copied. + */ + private ControlFlowBlock origBlock; + + /** + * The current block where statements are generated into. + */ + private ControlFlowBlock copyBlock; + + @Override + public ControlFlowGraph visitGraph(ControlFlowGraph origGraph) { + this.origGraph = origGraph; + // Copy all blocks + this.copyBlockMap = new HashMap<>(); + for (ControlFlowBlock origBlock : origGraph.getAllBlocks()) { + ControlFlowBlock copyBlock = visitBlock(origBlock); + if (copyBlock != null) { + copyBlockMap.put(copyBlock.getLabel(), copyBlock); + } + } + ControlFlowBlock copyFirstBlock = copyBlockMap.get(origGraph.getFirstBlock().getLabel()); + ControlFlowGraph copyGraph = new ControlFlowGraph(copyBlockMap, copyFirstBlock); + return copyGraph; + } + + @Override + public ControlFlowBlock visitBlock(ControlFlowBlock origBlock) { + Label label = origBlock.getLabel(); + ControlFlowBlock copyBlock = new ControlFlowBlock(label); + this.origBlock = origBlock; + this.copyBlock = copyBlock; + // Handle statements + List origBlockStatements = origBlock.getStatements(); + for (Statement origStatement : origBlockStatements) { + Statement copyStatement = visitStatement(origStatement); + if (copyStatement != null) { + this.copyBlock.addStatement(copyStatement); + } + } + // Handle successors + if (origBlock.getDefaultSuccessor() != null) { + this.copyBlock.setDefaultSuccessor(origBlock.getDefaultSuccessor()); + } + if (origBlock.getConditionalSuccessor() != null) { + this.copyBlock.setConditionalSuccessor(origBlock.getConditionalSuccessor()); + } + if (origBlock.getCallSuccessor() != null) { + this.copyBlock.setCallSuccessor(origBlock.getCallSuccessor()); + } + ControlFlowBlock result = this.copyBlock; + this.origBlock = null; + this.copyBlock = null; + return result; + } + + /** + * Adds an extra statement to the current block at the current generated position. + * The new statement is added beofre the copy currently being generated. + * + * @param statement The statement to add + */ + protected void addStatementToCurrentBlock(Statement statement) { + this.copyBlock.addStatement(statement); + } + + /** + * Splits the current block into two blocks in the generated graph. + * The new block will have the current block as predecessor and current the block will have the new block as default successor. + * + * @param label The label to use for the new block + * @return The new block. + */ + protected ControlFlowBlock splitCurrentBlock(Label label) { + ControlFlowBlock newBlock = new ControlFlowBlock(label); + this.copyBlock.setDefaultSuccessor(newBlock.getLabel()); + this.copyBlockMap.put(this.copyBlock.getLabel(), this.copyBlock); + this.copyBlock = newBlock; + return newBlock; + } + + /** + * Get the block currently being generated. + * + * @return The current block being generated into + */ + public ControlFlowBlock getCurrentBlock() { + return copyBlock; + } + + @Override + public Statement visitStatement(Statement statement) { + return (Statement) super.visitStatement(statement); + } + + @Override + public StatementPhi visitPhi(StatementPhi phi) { + VariableVersion lValue = phi.getLValue(); + StatementPhi copyPhi = new StatementPhi(lValue); + for (StatementPhi.PreviousSymbol origPreviousVersion : phi.getPreviousVersions()) { + RValue rValue = origPreviousVersion.getRValue(); + Label block = origPreviousVersion.getBlock(); + copyPhi.addPreviousVersion(block, rValue); + } + return copyPhi; + } + + @Override + public StatementAssignment visitAssignment(StatementAssignment origAssignment) { + LValue lValue = origAssignment.getLValue(); + RValue rValue1 = origAssignment.getRValue1(); + Operator operator = origAssignment.getOperator(); + RValue rValue2 = origAssignment.getRValue2(); + return new StatementAssignment(lValue, rValue1, operator, rValue2); + } + + @Override + public StatementConditionalJump visitConditionalJump(StatementConditionalJump origConditionalJump) { + RValue rValue1 = origConditionalJump.getRValue1(); + Operator operator = origConditionalJump.getOperator(); + RValue rValue2 = origConditionalJump.getRValue2(); + Label destination = origConditionalJump.getDestination(); + return new StatementConditionalJump(rValue1, operator, rValue2, destination); + } + + @Override + public StatementJump visitJump(StatementJump origJump) { + Label destination = origJump.getDestination(); + return new StatementJump(destination); + } + + @Override + public StatementLabel visitJumpTarget(StatementLabel origJump) { + Label label = origJump.getLabel(); + return new StatementLabel(label); + } + + @Override + public StatementCallLValue visitCallLValue(StatementCallLValue callLValue) { + LValue lValue = callLValue.getLValue(); + String procedureName = callLValue.getProcedureName(); + List parameters = callLValue.getParameters(); + return new StatementCallLValue(lValue, procedureName, parameters); + } + + @Override + public StatementProcedureBegin visitProcedureBegin(StatementProcedureBegin origProcedureBegin) { + return new StatementProcedureBegin(origProcedureBegin.getProcedure()); + } + + @Override + public StatementProcedureEnd visitProcedureEnd(StatementProcedureEnd origProcedureEnd) { + return new StatementProcedureEnd(origProcedureEnd.getProcedure()); + } + + @Override + public StatementReturn visitReturn(StatementReturn origReturn) { + return new StatementReturn(origReturn.getValue()); + } +} diff --git a/src/dk/camelot64/kickc/icl/Pass1GenerateControlFlowGraph.java b/src/dk/camelot64/kickc/icl/Pass1GenerateControlFlowGraph.java index 84908d818..2380c814c 100644 --- a/src/dk/camelot64/kickc/icl/Pass1GenerateControlFlowGraph.java +++ b/src/dk/camelot64/kickc/icl/Pass1GenerateControlFlowGraph.java @@ -28,15 +28,13 @@ public class Pass1GenerateControlFlowGraph { if(statement instanceof StatementLabel) { StatementLabel statementLabel = (StatementLabel) statement; ControlFlowBlock nextBlock = getOrCreateBlock(statementLabel.getLabel()); - currentBlock.setDefaultSuccessor(nextBlock); - nextBlock.addPredecessor(currentBlock); + currentBlock.setDefaultSuccessor(nextBlock.getLabel()); blockStack.pop(); blockStack.push(nextBlock); } else if(statement instanceof StatementJump) { StatementJump statementJump = (StatementJump) statement; ControlFlowBlock jmpBlock = getOrCreateBlock(statementJump.getDestination()); - currentBlock.setDefaultSuccessor(jmpBlock); - jmpBlock.addPredecessor(currentBlock); + currentBlock.setDefaultSuccessor(jmpBlock.getLabel()); ControlFlowBlock nextBlock = getOrCreateBlock(scope.addLabelIntermediate()); blockStack.pop(); blockStack.push(nextBlock); @@ -45,28 +43,30 @@ public class Pass1GenerateControlFlowGraph { StatementConditionalJump statementConditionalJump = (StatementConditionalJump) statement; ControlFlowBlock jmpBlock = getOrCreateBlock(statementConditionalJump.getDestination()); ControlFlowBlock nextBlock = getOrCreateBlock(scope.addLabelIntermediate()); - currentBlock.setDefaultSuccessor(nextBlock); - currentBlock.setConditionalSuccessor(jmpBlock); - nextBlock.addPredecessor(currentBlock); - jmpBlock.addPredecessor(currentBlock); + currentBlock.setDefaultSuccessor(nextBlock.getLabel()); + currentBlock.setConditionalSuccessor(jmpBlock.getLabel()); blockStack.pop(); blockStack.push(nextBlock); } else if(statement instanceof StatementProcedureBegin) { // Procedure strategy implemented is currently variable-based transfer of parameters/return values - StatementProcedureBegin procedure = (StatementProcedureBegin) statement; - procedure.setStrategy(StatementProcedureBegin.Strategy.PASS_BY_REGISTER); - Label procedureLabel = procedure.getProcedure().getLabel(); + StatementProcedureBegin procedureBegin = (StatementProcedureBegin) statement; + procedureBegin.setStrategy(StatementProcedureBegin.Strategy.PASS_BY_REGISTER); + Label procedureLabel = procedureBegin.getProcedure().getLabel(); ControlFlowBlock procBlock = getOrCreateBlock(procedureLabel); blockStack.push(procBlock); } else if(statement instanceof StatementProcedureEnd) { // Procedure strategy implemented is currently variable-based transfer of parameters/return values - currentBlock.setDefaultSuccessor(new ControlFlowBlock(new Label("@RETURN", scope, false))); + currentBlock.setDefaultSuccessor(new Label("@RETURN", scope, false)); ControlFlowBlock nextBlock = getOrCreateBlock(scope.addLabelIntermediate()); blockStack.pop(); - ControlFlowBlock prevBlock = blockStack.pop(); - prevBlock.setDefaultSuccessor(nextBlock); - nextBlock.addPredecessor(prevBlock); + ControlFlowBlock prevBlock = blockStack.pop(); + prevBlock.setDefaultSuccessor(nextBlock.getLabel()); blockStack.push(nextBlock); + } else if(statement instanceof StatementReturn) { + // Procedure strategy implemented is currently variable-based transfer of parameters/return values + StatementReturn aReturn = (StatementReturn) statement; + currentBlock.addStatement(aReturn); + // TODO: Make all returns exit through the same exit-block! } else { currentBlock.addStatement(statement); } diff --git a/src/dk/camelot64/kickc/icl/Pass1GenerateSingleStaticAssignmentForm.java b/src/dk/camelot64/kickc/icl/Pass1GenerateSingleStaticAssignmentForm.java index dfcae411e..17f974a34 100644 --- a/src/dk/camelot64/kickc/icl/Pass1GenerateSingleStaticAssignmentForm.java +++ b/src/dk/camelot64/kickc/icl/Pass1GenerateSingleStaticAssignmentForm.java @@ -28,7 +28,9 @@ public class Pass1GenerateSingleStaticAssignmentForm { } while (!done); } - /** Version all non-versioned non-intermediary being assigned a value. */ + /** + * Version all non-versioned non-intermediary being assigned a value. + */ private void versionAllAssignments() { for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { for (Statement statement : block.getStatements()) { @@ -41,12 +43,23 @@ public class Pass1GenerateSingleStaticAssignmentForm { VariableVersion version = symbols.createVersion(assignedSymbol); assignment.setLValue(version); } + } else if(statement instanceof StatementCallLValue) { + StatementCallLValue call = (StatementCallLValue) statement; + LValue lValue = call.getLValue(); + if (lValue instanceof VariableUnversioned) { + // Assignment to a non-versioned non-intermediary variable + VariableUnversioned assignedSymbol = (VariableUnversioned) lValue; + VariableVersion version = symbols.createVersion(assignedSymbol); + call.setLValue(version); + } } } } } - /** Version all uses of non-versioned non-intermediary variables */ + /** + * Version all uses of non-versioned non-intermediary variables + */ private void versionAllUses() { for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { // Newest version of variables in the block. @@ -105,9 +118,10 @@ public class Pass1GenerateSingleStaticAssignmentForm { /** * Find and return the latest version of an rValue (if it is a non-versioned symbol). * If a version is needed and no version is found a new version is created as a phi-function. - * @param rValue The rValue to examine + * + * @param rValue The rValue to examine * @param blockVersions The current version defined in the block for each symbol. - * @param blockNewPhis New versions to be created as phi-functions. Modified if a new phi-function needs to be created. + * @param blockNewPhis New versions to be created as phi-functions. Modified if a new phi-function needs to be created. * @return Null if the rValue does not need versioning. The versioned symbol to use if it does. */ private VariableVersion findOrCreateVersion( @@ -132,12 +146,14 @@ public class Pass1GenerateSingleStaticAssignmentForm { return version; } - /** Look through all new phi-functions and fill out their parameters. + /** + * Look through all new phi-functions and fill out their parameters. + * * @return true if all phis were completely filled out. * false if new phis were added, meaning another iteration is needed. - * */ + */ private boolean completePhiFunctions() { - Map> newPhis = new HashMap<>(); + Map> newPhis = new HashMap<>(); Map> symbolMap = buildSymbolMap(); for (ControlFlowBlock block : this.controlFlowGraph.getAllBlocks()) { for (Statement statement : block.getStatements()) { @@ -146,35 +162,36 @@ public class Pass1GenerateSingleStaticAssignmentForm { if (phi.getPreviousVersions().isEmpty()) { VariableVersion versioned = phi.getLValue(); VariableUnversioned unversioned = versioned.getVersionOf(); - for (ControlFlowBlock predecessor : block.getPredecessors()) { - Map predecessorMap = symbolMap.get(predecessor.getLabel()); + for (ControlFlowBlock predecessor : controlFlowGraph.getPredecessors(block)) { + Label predecessorLabel = predecessor.getLabel(); + Map predecessorMap = symbolMap.get(predecessorLabel); VariableVersion previousSymbol = null; if (predecessorMap != null) { previousSymbol = predecessorMap.get(unversioned); } - if (previousSymbol == null) { - // No previous symbol found in predecessor block. Look in new phi functions. - Map predecessorNewPhis = newPhis.get(predecessor); - if (predecessorNewPhis == null) { - predecessorNewPhis = new HashMap<>(); - newPhis.put(predecessor, predecessorNewPhis); - } - previousSymbol = predecessorNewPhis.get(unversioned); - if (previousSymbol == null) { - // No previous symbol found in predecessor block. Add a new phi function to the predecessor. - previousSymbol = symbols.createVersion(unversioned); - predecessorNewPhis.put(unversioned, previousSymbol); - } + if (previousSymbol == null) { + // No previous symbol found in predecessor block. Look in new phi functions. + Map predecessorNewPhis = newPhis.get(predecessorLabel); + if (predecessorNewPhis == null) { + predecessorNewPhis = new HashMap<>(); + newPhis.put(predecessorLabel, predecessorNewPhis); + } + previousSymbol = predecessorNewPhis.get(unversioned); + if (previousSymbol == null) { + // No previous symbol found in predecessor block. Add a new phi function to the predecessor. + previousSymbol = symbols.createVersion(unversioned); + predecessorNewPhis.put(unversioned, previousSymbol); } - phi.addPreviousVersion(predecessor, previousSymbol); } + phi.addPreviousVersion(predecessorLabel, previousSymbol); + } } } } } // Ads new phi functions to blocks for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { - Map blockNewPhis = newPhis.get(block); + Map blockNewPhis = newPhis.get(block.getLabel()); if (blockNewPhis != null) { for (VariableUnversioned symbol : blockNewPhis.keySet()) { block.addPhiStatement(blockNewPhis.get(symbol)); @@ -217,6 +234,21 @@ public class Pass1GenerateSingleStaticAssignmentForm { symbolMap.put(label, blockMap); } blockMap.put(unversioned, versioned); + } else if (statement instanceof StatementCallLValue) { + StatementCallLValue call = (StatementCallLValue) statement; + LValue lValue = call.getLValue(); + if (lValue instanceof VariableVersion) { + VariableVersion versioned = (VariableVersion) lValue; + Label label = block.getLabel(); + VariableUnversioned unversioned = versioned.getVersionOf(); + Map blockMap = symbolMap.get(label); + if (blockMap == null) { + blockMap = new HashMap<>(); + symbolMap.put(label, blockMap); + } + blockMap.put(unversioned, versioned); + } + } } } diff --git a/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java new file mode 100644 index 000000000..e5da502e9 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java @@ -0,0 +1,46 @@ +package dk.camelot64.kickc.icl; + +import java.util.List; + +/** Pass that modifies a control flow graph to call procedures by passing parameters through registers */ +public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { + + private Scope scope; + private ControlFlowGraph graph; + + public Pass1ProcedureCallParameters(Scope scope, ControlFlowGraph graph ) { + this.scope = scope; + this.graph= graph; + } + + public ControlFlowGraph generate() { + ControlFlowGraph generated = visitGraph(graph); + return generated; + } + + @Override + public StatementCallLValue visitCallLValue(StatementCallLValue origCall) { + // Procedure strategy implemented is currently variable-based transfer of parameters/return values + // Generate parameter passing assignments + Procedure procedure = origCall.getProcedure(); + List parameterDecls = procedure.getParameters(); + List parameterValues = origCall.getParameters(); + for (int i = 0; i < parameterDecls.size(); i++) { + Variable parameterDecl = parameterDecls.get(i); + RValue parameterValue = parameterValues.get(i); + addStatementToCurrentBlock(new StatementAssignment(parameterDecl, parameterValue)); + } + String procedureName = origCall.getProcedureName(); + Variable procReturnVar = procedure.getVariable("return"); + StatementCallLValue copyCall = new StatementCallLValue(procReturnVar, procedureName, null); + copyCall.setParametersByAssignment(true); + copyCall.setProcedure(procedure); + addStatementToCurrentBlock(copyCall); + getCurrentBlock().setCallSuccessor(procedure.getLabel()); + splitCurrentBlock(scope.addLabelIntermediate()); + addStatementToCurrentBlock(new StatementAssignment(origCall.getLValue(), procReturnVar)); + return null; + } + + +} diff --git a/src/dk/camelot64/kickc/icl/Pass1ProcedureCallsReturnValue.java b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallsReturnValue.java new file mode 100644 index 000000000..9712fa404 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallsReturnValue.java @@ -0,0 +1,38 @@ +package dk.camelot64.kickc.icl; + +/** Pass that modifies a control flow graph to call procedures by passing return value through registers */ +public class Pass1ProcedureCallsReturnValue extends ControlFlowGraphCopyVisitor { + + private Scope scope; + private ControlFlowGraph graph; + + public Pass1ProcedureCallsReturnValue(Scope scope, ControlFlowGraph graph ) { + this.scope = scope; + this.graph= graph; + } + + public ControlFlowGraph generate() { + ControlFlowGraph generated = visitGraph(graph); + return generated; + } + + @Override + public StatementCallLValue visitCallLValue(StatementCallLValue origCall) { + // Procedure strategy implemented is currently variable-based transfer of parameters/return values + // Generate return value assignment + Procedure procedure = origCall.getProcedure(); + + String procedureName = origCall.getProcedureName(); + StatementCallLValue copyCall = new StatementCallLValue(null, procedureName, null); + copyCall.setParametersByAssignment(true); + copyCall.setProcedure(procedure); + addStatementToCurrentBlock(copyCall); + getCurrentBlock().setCallSuccessor(procedure.getLabel()); + + //StatementAssignment returnAssignment = new StatementAssignment(); + + return null; + } + + +} diff --git a/src/dk/camelot64/kickc/icl/Pass1TypeInference.java b/src/dk/camelot64/kickc/icl/Pass1TypeInference.java index 02739a8b3..2de94bd97 100644 --- a/src/dk/camelot64/kickc/icl/Pass1TypeInference.java +++ b/src/dk/camelot64/kickc/icl/Pass1TypeInference.java @@ -47,6 +47,9 @@ public class Pass1TypeInference { String procedureName = call.getProcedureName(); Procedure procedure = scopes.peek().getProcedure(procedureName); call.setProcedure(procedure); + if(procedure.getParameters().size()!=call.getParameters().size()) { + throw new RuntimeException("Wrong number of parameters in call. Expected " +procedure.getParameters().size()+". "+statement.toString()); + } ((Variable) lValue).setInferredType(procedure.getReturnType()); } } diff --git a/src/dk/camelot64/kickc/icl/Pass2CullEmptyBlocks.java b/src/dk/camelot64/kickc/icl/Pass2CullEmptyBlocks.java index 207465142..1c1f05bb1 100644 --- a/src/dk/camelot64/kickc/icl/Pass2CullEmptyBlocks.java +++ b/src/dk/camelot64/kickc/icl/Pass2CullEmptyBlocks.java @@ -18,29 +18,23 @@ public class Pass2CullEmptyBlocks extends Pass2SsaOptimization { remove.add(block); } } - for (ControlFlowBlock block : remove) { - ControlFlowBlock successor = block.getDefaultSuccessor(); - for (ControlFlowBlock predecessor : block.getPredecessors()) { - replace.put(block.getLabel(), predecessor.getLabel()); - if (block.equals(predecessor.getDefaultSuccessor())) { - predecessor.setDefaultSuccessor(successor); - if (successor != null) { - successor.addPredecessor(predecessor); - } + for (ControlFlowBlock removeBlock : remove) { + ControlFlowBlock successor = getGraph().getDefaultSuccessor(removeBlock); + for (ControlFlowBlock predecessor : getGraph().getPredecessors(removeBlock)) { + replace.put(removeBlock.getLabel(), predecessor.getLabel()); + if (removeBlock.getLabel().equals(predecessor.getDefaultSuccessor())) { + predecessor.setDefaultSuccessor(successor.getLabel()); } - if (block.equals(predecessor.getConditionalSuccessor())) { - predecessor.setConditionalSuccessor(successor); - if (successor != null) { - successor.addPredecessor(predecessor); - } + if (removeBlock.getLabel().equals(predecessor.getConditionalSuccessor())) { + predecessor.setConditionalSuccessor(successor.getLabel()); + } + if (removeBlock.getLabel().equals(predecessor.getCallSuccessor())) { + predecessor.setCallSuccessor(successor.getLabel()); } } - if (successor != null && block.getLabel().isIntermediate()) { - successor.removePredecessor(block); - } - getGraph().getAllBlocks().remove(block); - getSymbols().remove(block.getLabel()); - System.out.println("Culled Empty Block " + block.getLabel()); + getGraph().getAllBlocks().remove(removeBlock); + getSymbols().remove(removeBlock.getLabel()); + System.out.println("Culled Empty Block " + removeBlock.getLabel()); } replaceLabels(replace); return remove.size()>0; diff --git a/src/dk/camelot64/kickc/icl/Pass2SsaOptimization.java b/src/dk/camelot64/kickc/icl/Pass2SsaOptimization.java index 101329107..76fe4370d 100644 --- a/src/dk/camelot64/kickc/icl/Pass2SsaOptimization.java +++ b/src/dk/camelot64/kickc/icl/Pass2SsaOptimization.java @@ -168,9 +168,9 @@ public abstract class Pass2SsaOptimization { @Override public Void visitPhi(StatementPhi phi) { for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) { - Label replacement = getReplacement(replacements, previousSymbol.getBlock().getLabel()); + Label replacement = getReplacement(replacements, previousSymbol.getBlock()); if (replacement != null) { - previousSymbol.setBlock(graph.getBlock(replacement)); + previousSymbol.setBlock(replacement); } } return null; diff --git a/src/dk/camelot64/kickc/icl/Pass3CodeGeneration.java b/src/dk/camelot64/kickc/icl/Pass3CodeGeneration.java index abbd4f9b5..43b5bcb99 100644 --- a/src/dk/camelot64/kickc/icl/Pass3CodeGeneration.java +++ b/src/dk/camelot64/kickc/icl/Pass3CodeGeneration.java @@ -27,7 +27,7 @@ public class Pass3CodeGeneration { // Generate statements genStatements(asm, block); // Generate exit - ControlFlowBlock defaultSuccessor = block.getDefaultSuccessor(); + ControlFlowBlock defaultSuccessor = graph.getDefaultSuccessor(block); if (defaultSuccessor != null) { if (defaultSuccessor.hasPhiStatements()) { genBlockPhiTransition(asm, block, defaultSuccessor); @@ -81,8 +81,8 @@ public class Pass3CodeGeneration { private void genBlockEntryPoints(AsmProgram asm, ControlFlowBlock block) { if (block.hasPhiStatements()) { - for (ControlFlowBlock predecessor : block.getPredecessors()) { - if (block.equals(predecessor.getConditionalSuccessor())) { + for (ControlFlowBlock predecessor : graph.getPredecessors(block)) { + if (block.getLabel().equals(predecessor.getConditionalSuccessor())) { genBlockPhiTransition(asm, predecessor, block); asm.addInstruction("JMP", AsmAddressingMode.ABS, block.getLabel().getLocalName().replace('@', 'B')); } diff --git a/src/dk/camelot64/kickc/icl/Scope.java b/src/dk/camelot64/kickc/icl/Scope.java index 88d1a3834..9aa3ed747 100644 --- a/src/dk/camelot64/kickc/icl/Scope.java +++ b/src/dk/camelot64/kickc/icl/Scope.java @@ -125,6 +125,7 @@ public class Scope implements Symbol { } Procedure procedure = new Procedure(name, type, this); add(procedure); + procedure.addVariable("return", type); return procedure; } diff --git a/src/dk/camelot64/kickc/icl/StatementCallLValue.java b/src/dk/camelot64/kickc/icl/StatementCallLValue.java index 88a67144a..2d26da32a 100644 --- a/src/dk/camelot64/kickc/icl/StatementCallLValue.java +++ b/src/dk/camelot64/kickc/icl/StatementCallLValue.java @@ -14,11 +14,13 @@ public class StatementCallLValue implements StatementLValue { private String procedureName; private List parameters; private Procedure procedure; + private boolean parametersByAssignment; public StatementCallLValue(LValue lValue, String procedureName, List parameters) { this.lValue = lValue; this.procedureName = procedureName; this.parameters = parameters; + this.parametersByAssignment = false; } public LValue getLValue() { @@ -53,6 +55,14 @@ public class StatementCallLValue implements StatementLValue { return parameters.get(idx); } + public boolean isParametersByAssignment() { + return parametersByAssignment; + } + + public void setParametersByAssignment(boolean parametersByAssignment) { + this.parametersByAssignment = parametersByAssignment; + } + @Override public String toString() { StringBuilder res = new StringBuilder(); @@ -64,10 +74,20 @@ public class StatementCallLValue implements StatementLValue { } else { res.append(procedureName + " "); } - for (RValue parameter : parameters) { - res.append(parameter+" "); + if(parameters!=null) { + for (RValue parameter : parameters) { + res.append(parameter + " "); + } + } + if(parametersByAssignment) { + res.append("param-assignment"); } return res.toString(); } + public void clearParameters() { + this.parameters = null; + this.parametersByAssignment = true; + } + } diff --git a/src/dk/camelot64/kickc/icl/StatementPhi.java b/src/dk/camelot64/kickc/icl/StatementPhi.java index f434fffd0..a82a8e07d 100644 --- a/src/dk/camelot64/kickc/icl/StatementPhi.java +++ b/src/dk/camelot64/kickc/icl/StatementPhi.java @@ -27,15 +27,15 @@ public class StatementPhi implements StatementLValue { * Which value is chosen depends on which block transition was made. */ public static class PreviousSymbol { - private ControlFlowBlock block; + private Label block; private RValue rValue; - public PreviousSymbol(ControlFlowBlock block, RValue rValue) { + public PreviousSymbol(Label block, RValue rValue) { this.block = block; this.rValue = rValue; } - public ControlFlowBlock getBlock() { + public Label getBlock() { return block; } @@ -43,12 +43,11 @@ public class StatementPhi implements StatementLValue { return rValue; } - public void setRValue(RValue RValue) { this.rValue = RValue; } - public void setBlock(ControlFlowBlock block) { + public void setBlock(Label block) { this.block = block; } } @@ -65,8 +64,8 @@ public class StatementPhi implements StatementLValue { this.lValue = (VariableVersion) lValue; } - public void addPreviousVersion(ControlFlowBlock block, VariableVersion symbol) { - previousVersions.add(new PreviousSymbol(block, symbol)); + public void addPreviousVersion(Label block, RValue rValue) { + previousVersions.add(new PreviousSymbol(block, rValue)); } public List getPreviousVersions() { @@ -78,7 +77,7 @@ public class StatementPhi implements StatementLValue { StringBuilder out = new StringBuilder(); out.append(lValue + " ← " + "phi("); for (PreviousSymbol previousSymbol : previousVersions) { - out.append(" "+previousSymbol.getBlock().getLabel().getLocalName()+"/"+previousSymbol.getRValue()); + out.append(" "+previousSymbol.getBlock().getLocalName()+"/"+previousSymbol.getRValue()); } out.append(" )"); return out.toString(); diff --git a/src/dk/camelot64/kickc/test/Main.java b/src/dk/camelot64/kickc/test/Main.java index 51aa63ca9..6c9b5e7f1 100644 --- a/src/dk/camelot64/kickc/test/Main.java +++ b/src/dk/camelot64/kickc/test/Main.java @@ -42,15 +42,25 @@ public class Main { System.out.println("INITIAL CONTROL FLOW GRAPH"); System.out.println(controlFlowGraph.toString()); - - - - if(1==1) return; + Pass1ProcedureCallParameters pass1ProcedureCallParameters = new Pass1ProcedureCallParameters(programScope, controlFlowGraph); + controlFlowGraph = pass1ProcedureCallParameters.generate(); + System.out.println("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL"); + System.out.println(controlFlowGraph.toString()); Pass1GenerateSingleStaticAssignmentForm pass1GenerateSingleStaticAssignmentForm = new Pass1GenerateSingleStaticAssignmentForm(programScope, controlFlowGraph); pass1GenerateSingleStaticAssignmentForm.generate(); + System.out.println("CONTROL FLOW GRAPH SSA"); + System.out.println(controlFlowGraph.toString()); + + //Pass1ProcedureCallsReturnValue pass1ProcedureCallsReturnValue = new Pass1ProcedureCallsReturnValue(programScope, controlFlowGraph); + //controlFlowGraph = pass1ProcedureCallsReturnValue.generate(); + //System.out.println("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL & RETURN"); + //System.out.println(controlFlowGraph.toString()); + + if(1==1) return; + List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(controlFlowGraph, programScope)); optimizations.add(new Pass2ConstantPropagation(controlFlowGraph, programScope));