diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java index 53b1a8e68..e0f9ff0a8 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java @@ -11,19 +11,19 @@ import java.util.*; */ public class ControlFlowGraph implements Graph { - private List blocks; + private List blocks; /** * Sequence of blocks used when generating ASM */ private List sequence; - public ControlFlowGraph(List blocks) { + public ControlFlowGraph(List blocks) { this.blocks = blocks; } - public ControlFlowBlock getBlock(LabelRef symbol) { - for(ControlFlowBlock block : blocks) { + public Graph.Block getBlock(LabelRef symbol) { + for(var block : blocks) { if(block.getLabel().equals(symbol)) { return block; } @@ -31,7 +31,7 @@ public class ControlFlowGraph implements Graph { return null; } - public void addBlock(ControlFlowBlock block) { + public void addBlock(Graph.Block block) { blocks.add(block); } @@ -39,14 +39,10 @@ public class ControlFlowGraph implements Graph { return Collections.unmodifiableList(blocks); } - public void setAllBlocks(List blocks) { - this.blocks = blocks; - } - public void remove(LabelRef label) { - ListIterator blocksIt = blocks.listIterator(); + ListIterator blocksIt = blocks.listIterator(); while(blocksIt.hasNext()) { - ControlFlowBlock block = blocksIt.next(); + var block = blocksIt.next(); if(block.getLabel().equals(label)) { blocksIt.remove(); return; @@ -54,23 +50,6 @@ public class ControlFlowGraph implements Graph { } } - public List getSequence() { - return sequence; - } - - public void setSequence(List sequence) { - if(sequence.size() != blocks.size()) { - throw new CompileError("ERROR! Sequence does not contain all blocks from the program. Sequence: " + sequence.size() + " Blocks: " + blocks.size()); - } - this.sequence = sequence; - ArrayList seqBlocks = new ArrayList<>(); - for(LabelRef labelRef : sequence) { - seqBlocks.add(getBlock(labelRef)); - } - this.blocks = seqBlocks; - } - - @Override public String toString() { return toString(null); @@ -95,7 +74,7 @@ public class ControlFlowGraph implements Graph { * Clear all statement indices, */ public void clearStatementIndices() { - for(Graph.Block block : getAllBlocks()) { + for(var block : getAllBlocks()) { for(Statement statement : block.getStatements()) { statement.setIndex(null); } diff --git a/src/main/java/dk/camelot64/kickc/model/Graph.java b/src/main/java/dk/camelot64/kickc/model/Graph.java index 38fcd6899..fd1c2e603 100644 --- a/src/main/java/dk/camelot64/kickc/model/Graph.java +++ b/src/main/java/dk/camelot64/kickc/model/Graph.java @@ -16,6 +16,8 @@ public interface Graph { List getAllBlocks(); + void addBlock(Graph.Block block); + default List getPredecessors(Graph.Block block) { ArrayList predecessorBlocks = new ArrayList<>(); for(Graph.Block other : getAllBlocks()) { diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index 56d6daef6..f0dfa6962 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -4,7 +4,6 @@ import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.OutputFileManager; import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer; -import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.values.LabelRef; @@ -158,12 +157,17 @@ public class Program { @Override public List getAllBlocks() { - return getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).map(ControlFlowGraph::getAllBlocks).flatMap(Collection::stream).collect(Collectors.toList()); + return getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).map(ControlFlowGraph::getAllBlocks).flatMap(Collection::stream).collect(Collectors.toUnmodifiableList()); + } + + @Override + public void addBlock(Block block) { + throw new CompileError("Internal Error! Cannot add blocks to the read-only total control flow graph!"); } }; } - private Collection getProcedureCompilations() { + public Collection getProcedureCompilations() { return procedureCompilations.values(); } diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java index ed32ce6ba..8d9d0d688 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java @@ -34,6 +34,19 @@ public abstract class Scope implements Symbol { setFullName(); } + /** + * Get the containing procedure. + * @return The procedure containing the scope. Null if the scope is not contained in a procedure. + */ + public Procedure getProcedure() { + if(this instanceof Procedure) + return (Procedure) this; + else if(this instanceof ProgramScope) + return null; + else + return this.getScope().getProcedure(); + } + private void setFullName() { String scopeName = (parentScope == null) ? "" : parentScope.getFullName(); fullName = (scopeName.length() > 0) ? scopeName + "::" + name : name; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertProcedureDefined.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertProcedureDefined.java index 99eb61d19..f28f90935 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertProcedureDefined.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertProcedureDefined.java @@ -1,9 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.ProcedureCompilation; -import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.symbols.Label; import dk.camelot64.kickc.model.symbols.Procedure; @@ -29,7 +26,7 @@ public class Pass1AssertProcedureDefined extends Pass1Base { final ProcedureCompilation procedureCompilation = getProgram().getProcedureCompilation(procedure.getRef()); if(procedureCompilation == null) throw new CompileError("Error! Function body is never defined: " + procedure.getFullName()); - final ControlFlowBlock procedureBlock = procedureCompilation.getGraph().getBlock(procedureLabel.getRef()); + final Graph.Block procedureBlock = procedureCompilation.getGraph().getBlock(procedureLabel.getRef()); if(procedureBlock == null) throw new CompileError("Error! Function body is never defined: " + procedure.getFullName()); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertReturn.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertReturn.java index 92b5b946f..92841d4db 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertReturn.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertReturn.java @@ -31,7 +31,7 @@ public class Pass1AssertReturn extends Pass1Base { final ProcedureCompilation procedureCompilation = getProgram().getProcedureCompilation(procedure.getRef()); final ControlFlowGraph graph = procedureCompilation.getGraph(); LabelRef entryLabel = procedure.getRef().getLabelRef(); - ControlFlowBlock entryBlock = graph.getBlock(entryLabel); + Graph.Block entryBlock = graph.getBlock(entryLabel); assertReturn(graph, entryBlock, new LinkedHashSet<>()); } } @@ -45,25 +45,23 @@ public class Pass1AssertReturn extends Pass1Base { * @param block The block to examine * @param visited Blocks already visited */ - public void assertReturn(ControlFlowGraph graph, ControlFlowBlock block, Collection visited) { + public void assertReturn(ControlFlowGraph graph, Graph.Block block, Collection visited) { if(visited.contains(block.getLabel())) { return; } visited.add(block.getLabel()); for(Statement statement : block.getStatements()) { - if(statement instanceof StatementAssignment) { - StatementAssignment assignment = (StatementAssignment) statement; + if(statement instanceof StatementAssignment assignment) { if(assignment.getlValue() instanceof VariableRef && ((VariableRef) assignment.getlValue()).getLocalName().equals("return")) { // Happy days - return found! return; } - } else if(statement instanceof StatementConditionalJump) { - StatementConditionalJump cond = (StatementConditionalJump) statement; - ControlFlowBlock jumpTo = graph.getBlock(cond.getDestination()); + } else if(statement instanceof StatementConditionalJump cond) { + Graph.Block jumpTo = graph.getBlock(cond.getDestination()); assertReturn(graph, jumpTo, visited); } } - ControlFlowBlock successor = graph.getBlock(block.getDefaultSuccessor()); + Graph.Block successor = graph.getBlock(block.getDefaultSuccessor()); if(successor == null || successor.getLabel().getLocalName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) { throw new CompileError("Error! Method must end with a return statement. " + block.getScope().toString(getProgram())); } else { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1CallPhiParameters.java b/src/main/java/dk/camelot64/kickc/passes/Pass1CallPhiParameters.java index 5e9faa663..aa73d3d9b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1CallPhiParameters.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1CallPhiParameters.java @@ -15,18 +15,28 @@ import java.util.*; /** Handle calling convention {@link Procedure.CallingConvention#PHI_CALL} by passing parameters through variables */ public class Pass1CallPhiParameters { - private Program program; + private final Program program; public Pass1CallPhiParameters(Program program) { this.program = program; } - private Map splitBlockMap = new LinkedHashMap<>(); - public void execute() { - final List todoBlocks = getGraph().getAllBlocks(); + for(ProcedureCompilation procedureCompilation : this.program.getProcedureCompilations()) { + handleProcedure(procedureCompilation); + } + } + + private void handleProcedure(ProcedureCompilation procedureCompilation) { + + final Graph graph = procedureCompilation.getGraph(); + + final List todoBlocks = new ArrayList<>(graph.getAllBlocks()); List doneBlocks = new ArrayList<>(); + Map splitBlockMap = new LinkedHashMap<>(); + + while(!todoBlocks.isEmpty()) { final Graph.Block block = todoBlocks.get(0); todoBlocks.remove(0); @@ -35,14 +45,13 @@ public class Pass1CallPhiParameters { final ListIterator stmtIt = block.getStatements().listIterator(); while(stmtIt.hasNext()) { Statement statement = stmtIt.next(); - if(statement instanceof StatementCall) { - StatementCall call = (StatementCall) statement; + if(statement instanceof StatementCall call) { // Generate parameter passing assignments ProcedureRef procedureRef = call.getProcedure(); Procedure procedure = getScope().getProcedure(procedureRef); // Handle PHI-calls if(Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention())) { - final ControlFlowBlock newBlock = handlePhiCall(call, procedure, stmtIt, block); + final ControlFlowBlock newBlock = handlePhiCall(call, procedure, stmtIt, block, splitBlockMap); // The current block was split into two blocks - add the new block at the front of the todoBlocks todoBlocks.add(0, newBlock); } @@ -57,18 +66,19 @@ public class Pass1CallPhiParameters { for(VariableRef modifiedVar : modifiedVars) { if(getScope().getVariable(modifiedVar).isKindLoadStore()) continue; - stmtIt.add(new StatementAssignment(modifiedVar, modifiedVar, false, ((StatementReturn) statement).getSource(), Comment.NO_COMMENTS)); + stmtIt.add(new StatementAssignment(modifiedVar, modifiedVar, false, statement.getSource(), Comment.NO_COMMENTS)); } stmtIt.next(); } } } } + // Update graph blocks to include the new blocks added - program.getGraph().setAllBlocks(doneBlocks); + procedureCompilation.setGraph(new ControlFlowGraph(doneBlocks)); // Fix phi predecessors for any blocks has a split block as predecessor - for(Graph.Block block : getGraph().getAllBlocks()) { + for(var block : procedureCompilation.getGraph().getAllBlocks()) { if(block.hasPhiBlock()) { for(StatementPhiBlock.PhiVariable phiVariable : block.getPhiBlock().getPhiVariables()) { for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { @@ -86,11 +96,7 @@ public class Pass1CallPhiParameters { return program.getScope(); } - private Graph getGraph() { - return program.getGraph(); - } - - private ControlFlowBlock handlePhiCall(StatementCall call, Procedure procedure, ListIterator stmtIt, Graph.Block block) { + private ControlFlowBlock handlePhiCall(StatementCall call, Procedure procedure, ListIterator stmtIt, Graph.Block block, Map splitBlockMap) { List parameterDefs = procedure.getParameters(); List parameterValues = call.getParameters(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateControlFlowGraph.java b/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateControlFlowGraph.java index 37b1e7b77..48d6f1265 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateControlFlowGraph.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateControlFlowGraph.java @@ -32,10 +32,10 @@ public class Pass1GenerateControlFlowGraph extends Pass1Base { // Empty procedures should not produce any blocks continue; - List blocks = new ArrayList<>(); + List blocks = new ArrayList<>(); - ControlFlowBlock currentBlock = null; - ControlFlowBlock procBlock = getOrCreateBlock(procedure.getLabel().getRef(), procedure.getRef(), blocks); + Graph.Block currentBlock; + Graph.Block procBlock = getOrCreateBlock(procedure.getLabel().getRef(), procedure.getRef(), blocks); currentBlock = procBlock; for(Statement statement : sequence.getStatements()) { Symbol currentBlockLabel = getProgram().getScope().getSymbol(currentBlock.getLabel()); @@ -50,29 +50,25 @@ public class Pass1GenerateControlFlowGraph extends Pass1Base { } else if(statement instanceof StatementProcedureEnd) { // Procedure strategy implemented is currently variable-based transfer of parameters/return values currentBlock.setDefaultSuccessor(new Label(SymbolRef.PROCEXIT_BLOCK_NAME, programScope, false).getRef()); - } else if(statement instanceof StatementLabel) { - StatementLabel statementLabel = (StatementLabel) statement; - ControlFlowBlock nextBlock = getOrCreateBlock(statementLabel.getLabel(), currentBlock.getScope(), blocks); + } else if(statement instanceof StatementLabel statementLabel) { + Graph.Block nextBlock = getOrCreateBlock(statementLabel.getLabel(), currentBlock.getScope(), blocks); nextBlock.setComments(statementLabel.getComments()); currentBlock.setDefaultSuccessor(nextBlock.getLabel()); currentBlock = nextBlock; - } else if(statement instanceof StatementJump) { - StatementJump statementJump = (StatementJump) statement; - ControlFlowBlock jmpBlock = getOrCreateBlock(statementJump.getDestination(), currentBlock.getScope(), blocks); + } else if(statement instanceof StatementJump statementJump) { + Graph.Block jmpBlock = getOrCreateBlock(statementJump.getDestination(), currentBlock.getScope(), blocks); currentBlock.setDefaultSuccessor(jmpBlock.getLabel()); - ControlFlowBlock nextBlock = getOrCreateBlock(currentBlockScope.addLabelIntermediate().getRef(), currentBlock.getScope(), blocks); + Graph.Block nextBlock = getOrCreateBlock(currentBlockScope.addLabelIntermediate().getRef(), currentBlock.getScope(), blocks); currentBlock = nextBlock; - } else if(statement instanceof StatementConditionalJump) { + } else if(statement instanceof StatementConditionalJump statementConditionalJump) { currentBlock.addStatement(statement); - StatementConditionalJump statementConditionalJump = (StatementConditionalJump) statement; - ControlFlowBlock jmpBlock = getOrCreateBlock(statementConditionalJump.getDestination(), currentBlock.getScope(), blocks); - ControlFlowBlock nextBlock = getOrCreateBlock(currentBlockScope.addLabelIntermediate().getRef(), currentBlock.getScope(), blocks); + Graph.Block jmpBlock = getOrCreateBlock(statementConditionalJump.getDestination(), currentBlock.getScope(), blocks); + Graph.Block nextBlock = getOrCreateBlock(currentBlockScope.addLabelIntermediate().getRef(), currentBlock.getScope(), blocks); currentBlock.setDefaultSuccessor(nextBlock.getLabel()); currentBlock.setConditionalSuccessor(jmpBlock.getLabel()); currentBlock = nextBlock; - } else if(statement instanceof StatementReturn) { + } else if(statement instanceof StatementReturn aReturn) { // Procedure strategy implemented is currently variable-based transfer of parameters/return values - StatementReturn aReturn = (StatementReturn) statement; currentBlock.addStatement(aReturn); } else { currentBlock.addStatement(statement); @@ -85,13 +81,13 @@ public class Pass1GenerateControlFlowGraph extends Pass1Base { return false; } - private ControlFlowBlock getOrCreateBlock(LabelRef label, ScopeRef scope, List blocks) { - for(ControlFlowBlock block : blocks) { + private Graph.Block getOrCreateBlock(LabelRef label, ScopeRef scope, List blocks) { + for(var block : blocks) { if(block.getLabel().equals(label)) { return block; } } - ControlFlowBlock block = new ControlFlowBlock(label, scope); + Graph.Block block = new ControlFlowBlock(label, scope); blocks.add(block); return block; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java index 1517540b2..dd690d12a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java @@ -23,16 +23,23 @@ public class Pass1ProcedureInline extends Pass1Base { @Override public boolean step() { - List allBlocks = getGraph().getAllBlocks(); - ListIterator blocksIt = allBlocks.listIterator(); + for(var procedureCompilation : getProgram().getProcedureCompilations()) + if(inlineProcedure(procedureCompilation)) + return true; + return false; + } + + private boolean inlineProcedure(ProcedureCompilation procedureCompilation) { + final ControlFlowGraph procedureGraph = procedureCompilation.getGraph(); + final List procedureBlocks = new ArrayList<>(procedureGraph.getAllBlocks()); + ListIterator blocksIt = procedureBlocks.listIterator(); while(blocksIt.hasNext()) { Graph.Block block = blocksIt.next(); List blockStatements = block.getStatements(); ListIterator statementsIt = blockStatements.listIterator(); while(statementsIt.hasNext()) { Statement statement = statementsIt.next(); - if(statement instanceof StatementCall) { - StatementCall call = (StatementCall) statement; + if(statement instanceof StatementCall call) { ProcedureRef procedureRef = call.getProcedure(); Procedure procedure = getProgramScope().getProcedure(procedureRef); if(procedure.isDeclaredInline()) { @@ -41,6 +48,8 @@ public class Pass1ProcedureInline extends Pass1Base { throw new CompileError("Error! Interrupts cannot be inlined. "+procedure.getRef().toString()); } inlineProcedureCall(call, procedure, statementsIt, block, blocksIt); + // Update the procedure graph + procedureCompilation.setGraph(new ControlFlowGraph(procedureBlocks)); // Exit and restart return true; } @@ -184,24 +193,17 @@ public class Pass1ProcedureInline extends Pass1Base { */ private Statement inlineStatement(Statement procStatement, Procedure procedure, Scope callScope, int serial) { Statement inlinedStatement; - if(procStatement instanceof StatementAssignment) { - StatementAssignment procAssignment = (StatementAssignment) procStatement; + if(procStatement instanceof StatementAssignment procAssignment) { inlinedStatement = new StatementAssignment(procAssignment.getlValue(), procAssignment.getrValue1(), procAssignment.getOperator(), procAssignment.getrValue2(), procAssignment.isInitialAssignment(), procAssignment.getSource(), Comment.NO_COMMENTS); - } else if(procStatement instanceof StatementCall) { - StatementCall procCall = (StatementCall) procStatement; + } else if(procStatement instanceof StatementCall procCall) { StatementCall inlinedCall = new StatementCall(procCall.getlValue(), procCall.getProcedureName(), new ArrayList<>(procCall.getParameters()), procCall.getSource(), procCall.getComments()); inlinedCall.setProcedure(procCall.getProcedure()); inlinedStatement = inlinedCall; - } else if(procStatement instanceof StatementAsm) { - StatementAsm procAsm = (StatementAsm) procStatement; - StatementAsm inlinedAsm = new StatementAsm(procAsm.getAsmLines(), new LinkedHashMap<>(procAsm.getReferenced()), procAsm.getDeclaredClobber(), procAsm.getSource(), Comment.NO_COMMENTS); - inlinedStatement = inlinedAsm; - } else if(procStatement instanceof StatementKickAsm) { - StatementKickAsm procKasm = (StatementKickAsm) procStatement; - StatementKickAsm inlinedAsm = new StatementKickAsm(procKasm.getKickAsmCode(), procKasm.getBytes(), procKasm.getCycles(), procKasm.getUses(), procKasm.getDeclaredClobber(), procKasm.getSource(), Comment.NO_COMMENTS); - inlinedStatement = inlinedAsm; - } else if(procStatement instanceof StatementConditionalJump) { - StatementConditionalJump procConditional = (StatementConditionalJump) procStatement; + } else if(procStatement instanceof StatementAsm procAsm) { + inlinedStatement = new StatementAsm(procAsm.getAsmLines(), new LinkedHashMap<>(procAsm.getReferenced()), procAsm.getDeclaredClobber(), procAsm.getSource(), Comment.NO_COMMENTS); + } else if(procStatement instanceof StatementKickAsm procKasm) { + inlinedStatement = new StatementKickAsm(procKasm.getKickAsmCode(), procKasm.getBytes(), procKasm.getCycles(), procKasm.getUses(), procKasm.getDeclaredClobber(), procKasm.getSource(), Comment.NO_COMMENTS); + } else if(procStatement instanceof StatementConditionalJump procConditional) { LabelRef procDestinationRef = procConditional.getDestination(); Label procDestination = getProgramScope().getLabel(procDestinationRef); Label inlinedDest = procDestination; @@ -218,9 +220,7 @@ public class Pass1ProcedureInline extends Pass1Base { } else { throw new CompileError("Statement type of Inline function not handled " + procStatement, procStatement.getSource()); } - if(inlinedStatement!=null) { - ProgramValueIterator.execute(inlinedStatement, new RValueInliner(procedure, serial, callScope), null, null); - } + ProgramValueIterator.execute(inlinedStatement, new RValueInliner(procedure, serial, callScope), null, null); return inlinedStatement; } @@ -246,8 +246,7 @@ public class Pass1ProcedureInline extends Pass1Base { @Override public void execute(ProgramValue programValue, Statement currentStmt, ListIterator stmtIt, Graph.Block currentBlock) { Value rValue = programValue.get(); - if(rValue instanceof VariableRef) { - VariableRef procVarRef = (VariableRef) rValue; + if(rValue instanceof VariableRef procVarRef) { Variable procVar = Pass1ProcedureInline.this.getProgramScope().getVariable(procVarRef); if(procVar.getScope().equals(procedure)) { String inlineSymbolName = Pass1ProcedureInline.this.getInlineSymbolName(procedure, procVar, serial); @@ -299,8 +298,7 @@ public class Pass1ProcedureInline extends Pass1Base { callScope.addLabel(procedure.getLocalName() + serial); // And copy all procedure symbols for(Symbol procSymbol : procedure.getAllSymbols()) { - if(procSymbol instanceof Variable) { - Variable procVar = (Variable) procSymbol; + if(procSymbol instanceof Variable procVar) { String inlineVarName = getInlineSymbolName(procedure, procSymbol, serial); Variable inlineVar = Variable.createCopy(inlineVarName, callScope, procVar); callScope.add(inlineVar); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java index 6c3b00ca2..7854f98c8 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java @@ -28,14 +28,22 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { @Override public boolean step() { + boolean modified = false; + for(ProcedureCompilation procedureCompilation : getProgram().getProcedureCompilations()) { + modified |= findAndRewriteBooleanConditions(procedureCompilation.getGraph()); + } + return modified; + } + + private boolean findAndRewriteBooleanConditions(Graph graph) { boolean done = false; boolean modified = false; while(!done) { - VariableRef obsoleteConditionVar = findAndRewriteBooleanCondition(); + VariableRef obsoleteConditionVar = findAndRewriteBooleanCondition(graph); if(obsoleteConditionVar != null) { Collection obsoleteVars = new ArrayList<>(); obsoleteVars.add(obsoleteConditionVar); - removeAssignments(getGraph(), obsoleteVars); + removeAssignments(graph, obsoleteVars); deleteSymbols(getProgramScope(), obsoleteVars); modified = true; } else { @@ -46,17 +54,17 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { } /** - * Look through the entire program looking for an if() condition that uses &&, || or !. + * Look through a control flow graph looking for an if() condition that uses &&, || or !. * When found rewrite it (adding blocks) * * @return Null if no condition was found to rewrite. The now obsolete variable containing the && / || / ! to be removed. + * @param graph The control flow graph to modify */ - private VariableRef findAndRewriteBooleanCondition() { + private VariableRef findAndRewriteBooleanCondition(Graph graph) { final VariableReferenceInfos variableReferenceInfos = getProgram().getVariableReferenceInfos(); - for(var block : getGraph().getAllBlocks()) { + for(var block : graph.getAllBlocks()) { for(Statement statement : block.getStatements()) { - if(statement instanceof StatementConditionalJump) { - StatementConditionalJump conditional = (StatementConditionalJump) statement; + if(statement instanceof StatementConditionalJump conditional) { if(conditional.getrValue1() == null && conditional.getOperator() == null) { RValue conditionRValue = conditional.getrValue2(); if(conditionRValue instanceof VariableRef) { @@ -65,16 +73,15 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { VariableRef conditionVar = (VariableRef) conditionRValue; final Integer conditionDefineStatementIdx = variableReferenceInfos.getVarDefineStatement(conditionVar); if(conditionDefineStatementIdx != null) { - final Statement conditionDefineStatement = getGraph().getStatementByIndex(conditionDefineStatementIdx); - if(conditionDefineStatement instanceof StatementAssignment) { - StatementAssignment conditionAssignment = (StatementAssignment) conditionDefineStatement; + final Statement conditionDefineStatement = graph.getStatementByIndex(conditionDefineStatementIdx); + if(conditionDefineStatement instanceof StatementAssignment conditionAssignment) { if(Operators.LOGIC_AND.equals(conditionAssignment.getOperator())) { // Found if() with logical && condition - rewrite to if(c1) if(c2) { xx } - rewriteLogicAnd(block, conditional, conditionAssignment); + rewriteLogicAnd(block, conditional, conditionAssignment, getGraph()); return conditionVar; } else if(Operators.LOGIC_OR.equals(conditionAssignment.getOperator())) { // Found if() with logical || condition - rewrite to if(c1) goto x else if(c2) goto x else goto end, x:{ xx } end: - rewriteLogicOr(block, conditional, conditionAssignment); + rewriteLogicOr(block, conditional, conditionAssignment, getGraph()); return conditionVar; } else if(Operators.LOGIC_NOT.equals(conditionAssignment.getOperator())) { // Found if() with logical ! condition - rewrite to if(!c1) goto x else goto end, x:{ xx } end: @@ -98,8 +105,9 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { * @param block The block containing the current if() * @param conditional The if()-statement * @param conditionAssignment The assignment defining the condition variable. + * @param graph The control flow graph to modify */ - private void rewriteLogicAnd(Graph.Block block, StatementConditionalJump conditional, StatementAssignment conditionAssignment) { + private void rewriteLogicAnd(Graph.Block block, StatementConditionalJump conditional, StatementAssignment conditionAssignment, Graph graph) { // Found an if with a logical && condition - rewrite to if(c1) if(c2) { xx } getLog().append("Rewriting && if()-condition to two if()s " + conditionAssignment.toString(getProgram(), false)); ScopeRef currentScopeRef = block.getScope(); @@ -107,7 +115,7 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { // Add a new block containing the second part of the && condition expression Label newBlockLabel = currentScope.addLabelIntermediate(); ControlFlowBlock newBlock = new ControlFlowBlock(newBlockLabel.getRef(), currentScopeRef); - getGraph().addBlock(newBlock); + graph.addBlock(newBlock); LabelRef destLabel = conditional.getDestination(); StatementConditionalJump newConditional = new StatementConditionalJump(conditionAssignment.getrValue2(), destLabel, conditional.getSource(), Comment.NO_COMMENTS); newConditional.setDeclaredUnroll(conditional.isDeclaredUnroll()); @@ -120,7 +128,7 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { conditional.setrValue2(conditionAssignment.getrValue1()); // Replace the phi labels inside the destination block with the new block - Graph.Block destBlock = getGraph().getBlock(destLabel); + Graph.Block destBlock = graph.getBlock(destLabel); LinkedHashMap replacements = new LinkedHashMap<>(); replacements.put(block.getLabel(), newBlockLabel.getRef()); replaceLabels(destBlock.getPhiBlock(), replacements); @@ -133,15 +141,16 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { * @param block The block containing the current if() * @param conditional The if()-statement * @param conditionAssignment The assignment defining the condition variable. + * @param graph The control flow graph to modify */ - private void rewriteLogicOr(Graph.Block block, StatementConditionalJump conditional, StatementAssignment conditionAssignment) { + private void rewriteLogicOr(Graph.Block block, StatementConditionalJump conditional, StatementAssignment conditionAssignment, Graph graph) { getLog().append("Rewriting || if()-condition to two if()s " + conditionAssignment.toString(getProgram(), false)); ScopeRef currentScopeRef = block.getScope(); Scope currentScope = getProgramScope().getScope(currentScopeRef); // Add a new block containing the second part of the && condition expression Label newBlockLabel = currentScope.addLabelIntermediate(); ControlFlowBlock newBlock = new ControlFlowBlock(newBlockLabel.getRef(), currentScopeRef); - getGraph().addBlock(newBlock); + graph.addBlock(newBlock); StatementConditionalJump newConditional = new StatementConditionalJump(conditionAssignment.getrValue2(), conditional.getDestination(), conditional.getSource(), Comment.NO_COMMENTS); // Copy unrolling to the new conditional newConditional.setDeclaredUnroll(conditional.isDeclaredUnroll()); @@ -155,7 +164,7 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { conditional.setDeclaredUnroll(false); // Update the default destination PHI block to reflect the last of the conditions - Graph.Block defaultDestBlock = getGraph().getBlock(newBlock.getDefaultSuccessor()); + Graph.Block defaultDestBlock = graph.getBlock(newBlock.getDefaultSuccessor()); if(defaultDestBlock.hasPhiBlock()) { StatementPhiBlock defaultDestPhiBlock = defaultDestBlock.getPhiBlock(); for(StatementPhiBlock.PhiVariable phiVariable : defaultDestPhiBlock.getPhiVariables()) { @@ -168,13 +177,13 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { } } - Graph.Block conditionalDestBlock = getGraph().getBlock(conditional.getDestination()); + Graph.Block conditionalDestBlock = graph.getBlock(conditional.getDestination()); if(conditionalDestBlock.hasPhiBlock()) { StatementPhiBlock conditionalDestPhiBlock = conditionalDestBlock.getPhiBlock(); for(StatementPhiBlock.PhiVariable phiVariable : conditionalDestPhiBlock.getPhiVariables()) { for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { if(phiRValue.getPredecessor().equals(block.getLabel())) { - // Found phi-variable with current block as predecessor - copy the phivalue for the new block + // Found phi-variable with current block as predecessor - copy the phi-value for the new block phiVariable.setrValue(newBlockLabel.getRef(), phiRValue.getrValue()); break; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java index 5fb671c7f..ae2cc3207 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java @@ -71,7 +71,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { private void removeParameters(Set inlineRefs) { for(ConstantRef inlineRef : inlineRefs) { final Scope scope = getProgramScope().getConstant(inlineRef).getScope(); - final Procedure procedure = getProcedure(scope); + final Procedure procedure = scope.getProcedure(); if(procedure!=null) { final List parameters = procedure.getParameters(); final boolean modified = parameters.removeIf(param -> param.getRef().equals(inlineRef)); @@ -83,15 +83,6 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { } } - private static Procedure getProcedure(Scope scope) { - if(scope instanceof Procedure) - return (Procedure) scope; - else if(scope instanceof ProgramScope) - return null; - else - return getProcedure(scope.getScope()); - } - /** * Replace any aliases within the constant values themselves * diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java b/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java index 385e132ae..ab0e077d3 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java @@ -1,10 +1,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.CompileLog; -import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.Graph; -import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.Label; import dk.camelot64.kickc.model.symbols.Procedure; @@ -14,7 +11,7 @@ import dk.camelot64.kickc.model.values.*; import java.util.*; -/** Pass that eliminates constant if's - they are either removed (if false) or replaces the default successor (if true). */ +/** Pass that eliminates blocks that are not referenced. */ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { public Pass2EliminateUnusedBlocks(Program program) { @@ -28,9 +25,13 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { for(var block : getGraph().getAllBlocks()) { if(!referencedBlocks.contains(block.getLabel())) { unusedBlocks.add(block.getLabel()); + } + } + + for(var block : getGraph().getAllBlocks()) { + if(unusedBlocks.contains(block.getLabel())) { for(Statement stmt : block.getStatements()) { - if(stmt instanceof StatementLValue) { - StatementLValue assignment = (StatementLValue) stmt; + if(stmt instanceof StatementLValue assignment) { LValue lValue = assignment.getlValue(); if(lValue instanceof VariableRef) { Variable variable = getProgramScope().getVariable((VariableRef) lValue); @@ -52,12 +53,14 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { } } - Set removedBlocks = new HashSet<>(); for(LabelRef unusedBlock : unusedBlocks) { Symbol unusedSymbol = getProgramScope().getSymbol(unusedBlock); if(unusedSymbol instanceof Label) { - getGraph().remove(unusedBlock); + final Procedure procedure = unusedSymbol.getScope().getProcedure(); + final ProcedureCompilation procedureCompilation = getProgram().getProcedureCompilation(procedure.getRef()); + final ControlFlowGraph procedureGraph = procedureCompilation.getGraph(); + procedureGraph.remove(unusedBlock); Label label = getProgramScope().getLabel(unusedBlock); label.getScope().remove(label); removePhiRValues(unusedBlock, getProgram()); @@ -85,31 +88,19 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { public static void removeProcedure(ProcedureRef procedureRef, Set removedBlocks, Program program) { program.getLog().append("Removing unused procedure " + procedureRef); Procedure unusedProcedure = program.getScope().getProcedure(procedureRef); - List procedureBlocks = program.getGraph().getScopeBlocks(procedureRef); + final ProcedureCompilation procedureCompilation = program.getProcedureCompilation(procedureRef); + final ControlFlowGraph procedureGraph = procedureCompilation.getGraph(); + List procedureBlocks = new ArrayList<>(procedureGraph.getAllBlocks()); for(var procedureBlock : procedureBlocks) { LabelRef blockLabelRef = procedureBlock.getLabel(); program.getLog().append("Removing unused procedure block " + blockLabelRef); - program.getGraph().remove(blockLabelRef); + procedureGraph.remove(blockLabelRef); removePhiRValues(blockLabelRef, program); removedBlocks.add(blockLabelRef); } unusedProcedure.getScope().remove(unusedProcedure); } - /** - * Get all referenced blocks in the entire program - * - * @return All blocks referenced - */ - private Set getReferencedBlocks() { - Set referencedBlocks = new LinkedHashSet<>(); - List entryPointBlocks = getProgram().getEntryPointBlocks(); - for(var entryPointBlock : entryPointBlocks) { - findReferencedBlocks(entryPointBlock, referencedBlocks); - } - - return referencedBlocks; - } /** * Remove all PHI RValues in any phi-statements referencing the passed block @@ -143,6 +134,21 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { } } + /** + * Get all referenced blocks in the entire program + * + * @return All blocks referenced + */ + private Set getReferencedBlocks() { + Set referencedBlocks = new LinkedHashSet<>(); + List entryPointBlocks = getProgram().getEntryPointBlocks(); + for(var entryPointBlock : entryPointBlocks) { + findReferencedBlocks(entryPointBlock, referencedBlocks); + } + + return referencedBlocks; + } + /** * Find all referenced block labels recursively * diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiLifting.java b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiLifting.java index 9d0effdeb..c9e7afd64 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiLifting.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiLifting.java @@ -31,10 +31,16 @@ public class Pass3PhiLifting { } public void perform() { - Graph graph = program.getGraph(); + for(var procedureCompilation : program.getProcedureCompilations()) { + upliftProcedure(procedureCompilation); + } + } + + private void upliftProcedure(ProcedureCompilation procedureCompilation) { + final ControlFlowGraph graph = procedureCompilation.getGraph(); + List liftedBlocks = new ArrayList<>(graph.getAllBlocks()); ProgramScope programScope = program.getScope(); - List blocks = graph.getAllBlocks(); - ListIterator blocksIt = blocks.listIterator(); + ListIterator blocksIt = liftedBlocks.listIterator(); while(blocksIt.hasNext()) { Graph.Block block = blocksIt.next(); // Maps old predecessors to new blocks created @@ -45,7 +51,11 @@ public class Pass3PhiLifting { for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { if(!(phiRValue.getrValue() instanceof ConstantValue)) { LabelRef predecessorRef = phiRValue.getPredecessor(); - Graph.Block predecessorBlock = graph.getBlock(predecessorRef); + Graph.Block predecessorBlock = getBlock(liftedBlocks, predecessorRef); + if(predecessorBlock==null) { + // Look for the predecessor in the entire graph + predecessorBlock = program.getGraph().getBlock(predecessorRef); + } //VariableRef rValVarRef = (VariableRef) phiRValue.getrValue(); Variable newVar; if(phiVariable.getVariable().isVersion()) { @@ -88,7 +98,7 @@ public class Pass3PhiLifting { } program.getLog().append("Added new block during phi lifting " + newBlock.getLabel() + "(between " + predecessorRef + " and " + block.getLabel() + ")"); } else { - newBlock = graph.getBlock(newBlockRef); + newBlock = getBlock(liftedBlocks, newBlockRef); } List newBlockStatements = newBlock.getStatements(); newBlockStatements.add(newAssignment); @@ -121,6 +131,14 @@ public class Pass3PhiLifting { } } } + + // Update the procedure with the PHI-lifted blocks + procedureCompilation.setGraph(new ControlFlowGraph(liftedBlocks)); + + } + + private Graph.Block getBlock(List blocks, LabelRef blockRef) { + return blocks.stream().filter(block -> block.getLabel().equals(blockRef)).findFirst().orElse(null); } } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java b/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java index 00866461b..c9b9bf62d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java @@ -1,12 +1,12 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.Graph; -import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.model.values.LabelRef; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** Plan the optimal sequence for the blocks of the control flow graph */ public class PassNBlockSequencePlanner extends Pass2SsaOptimization { @@ -62,7 +62,7 @@ public class PassNBlockSequencePlanner extends Pass2SsaOptimization { } } - getGraph().setSequence(sequence); + setSequence(sequence); if(getLog().isVerboseSequencePlan()) { StringBuilder entry = new StringBuilder(); @@ -78,6 +78,21 @@ public class PassNBlockSequencePlanner extends Pass2SsaOptimization { } + public void setSequence(List sequence) { + if(sequence.size() != getGraph().getAllBlocks().size()) { + throw new CompileError("ERROR! Sequence does not contain all blocks from the program. Sequence: " + sequence.size() + " Blocks: " + getGraph().getAllBlocks().size()); + } + for(var procedureCompilation : getProgram().getProcedureCompilations()) { + final ControlFlowGraph procedureGraph = procedureCompilation.getGraph(); + final List procedureLabels = procedureGraph.getAllBlocks().stream().map(Graph.Block::getLabel).toList(); + final List procedureSequence = sequence.stream().filter(procedureLabels::contains).map(procedureGraph::getBlock).toList(); + if(procedureSequence.size() != procedureGraph.getAllBlocks().size()) { + throw new CompileError("ERROR! Sequence does not contain all blocks for "+procedureCompilation.getProcedureRef()+". Sequence: " + procedureSequence.size() + " Blocks: " + procedureGraph.getAllBlocks().size()); + } + procedureCompilation.setGraph(new ControlFlowGraph(procedureSequence)); + } + } + void pushTodo(Graph.Block block) { LabelRef blockRef = block.getLabel(); Scope blockScope = getProgramScope().getSymbol(blockRef).getScope(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java index 774b325d8..a563ab274 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java @@ -1,10 +1,10 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.GraphBaseVisitor; -import dk.camelot64.kickc.model.Graph; -import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.symbols.Label; +import dk.camelot64.kickc.model.symbols.Procedure; +import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.RValue; @@ -100,10 +100,19 @@ public class PassNCullEmptyBlocks extends Pass2SsaOptimization { } replaceLabels(predecessor, replace); } - getGraph().getAllBlocks().remove(removeBlock); + + + LabelRef removeBlockLabelRef = removeBlock.getLabel(); Label removeBlockLabel = getProgramScope().getLabel(removeBlockLabelRef); - removeBlockLabel.getScope().remove(removeBlockLabel); + final Scope removeBlockScope = removeBlockLabel.getScope(); + final Procedure procedure = removeBlockScope.getProcedure(); + final ProcedureCompilation procedureCompilation = getProgram().getProcedureCompilation(procedure.getRef()); + final List updatedBlocks = new ArrayList<>(procedureCompilation.getGraph().getAllBlocks()); + updatedBlocks.removeIf(block -> block.getLabel().equals(removeBlockLabelRef)); + procedureCompilation.setGraph(new ControlFlowGraph(updatedBlocks)); + + removeBlockScope.remove(removeBlockLabel); if(!pass1) getLog().append("Culled Empty Block " + removeBlockLabel.toString(getProgram())); }