diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java index 6ec6a276e..53b1a8e68 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java @@ -2,7 +2,6 @@ package dk.camelot64.kickc.model; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.values.LabelRef; -import dk.camelot64.kickc.model.values.ProcedureRef; import java.util.*; @@ -103,55 +102,4 @@ public class ControlFlowGraph implements Graph { } } - /** - * Get all statements executed between two statements (none of these are included in the result) - * - * @param from The statement to start at - * @param to The statement to end at - * @return All statements executed between the two passed statements - */ - public Collection getStatementsBetween(Statement from, Statement to, StatementInfos statementInfos) { - Collection between = new LinkedHashSet<>(); - final Graph.Block block = statementInfos.getBlock(from); - populateStatementsBetween(from, to, false, between, block); - return between; - } - - /** - * Fill the between collection with all statements executed between two statements (none of these are included in the result) - * - * @param from The statement to start at - * @param to The statement to end at - * @param between The between collection - * @param block The block to start from - */ - private void populateStatementsBetween(Statement from, Statement to, boolean isBetween, Collection between, Graph.Block block) { - for(Statement statement : block.getStatements()) { - if(between.contains(statement)) - // Stop infinite recursion - return; - if(isBetween) { - if(statement.equals(to)) - // The end was reached! - isBetween = false; - else - // We are between - add the statement - between.add(statement); - } else { - if(statement.equals(from)) - // We are now between! - isBetween = true; - } - } - if(isBetween) { - // Recurse to successor blocks - final Collection successors = block.getSuccessors(); - for(LabelRef successor : successors) { - if(successor.getFullName().equals(ProcedureRef.PROCEXIT_BLOCK_NAME)) - continue; - final ControlFlowBlock successorBlock = getBlock(successor); - populateStatementsBetween(from, to, true, between, successorBlock); - } - } - } } diff --git a/src/main/java/dk/camelot64/kickc/model/Graph.java b/src/main/java/dk/camelot64/kickc/model/Graph.java index 94b2e3862..1e1d2a0db 100644 --- a/src/main/java/dk/camelot64/kickc/model/Graph.java +++ b/src/main/java/dk/camelot64/kickc/model/Graph.java @@ -157,6 +157,22 @@ public interface Graph { return successors; } + public List getComments(); + + void setComments(List comments); + + void setDefaultSuccessor(LabelRef defaultSuccessor); + + void setConditionalSuccessor(LabelRef conditionalSuccessor); + + void setCallSuccessor(LabelRef callSuccessor); + + void addStatement(Statement statement); + + void addStatementAfter(Statement newStatement, Statement predecessor); + + void addStatementBeforeCall(Statement newStatement); + String toString(Program program, Graph graph); } diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java b/src/main/java/dk/camelot64/kickc/model/GraphBaseVisitor.java similarity index 98% rename from src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java rename to src/main/java/dk/camelot64/kickc/model/GraphBaseVisitor.java index a5e54eccc..900ac60dc 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java +++ b/src/main/java/dk/camelot64/kickc/model/GraphBaseVisitor.java @@ -5,7 +5,7 @@ import dk.camelot64.kickc.model.statements.*; import java.util.Collection; /** Base visitor for iterating through a control flow graph */ -public class ControlFlowGraphBaseVisitor { +public class GraphBaseVisitor { public T visitGraph(Graph graph) { Collection blocks = graph.getAllBlocks(); diff --git a/src/main/java/dk/camelot64/kickc/model/StatementsBetween.java b/src/main/java/dk/camelot64/kickc/model/StatementsBetween.java new file mode 100644 index 000000000..256c8240d --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/StatementsBetween.java @@ -0,0 +1,62 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.values.LabelRef; +import dk.camelot64.kickc.model.values.ProcedureRef; + +import java.util.Collection; +import java.util.LinkedHashSet; + +public class StatementsBetween { + /** + * Get all statements executed between two statements (none of these are included in the result) + * + * @param from The statement to start at + * @param to The statement to end at + * @return All statements executed between the two passed statements + */ + public static Collection find(Statement from, Graph.Block fromBlock, Statement to, Graph graph) { + Collection between = new LinkedHashSet<>(); + populateStatementsBetween(fromBlock, from, to, false, between, graph); + return between; + } + + /** + * Fill the between collection with all statements executed between two statements (none of these are included in the result) + * + * @param block The block to start from + * @param from The statement where we start counting (not included) + * @param to The statement to end at (not included) + * @param isBetween Are we already between from and to? + * @param between The between collection, being populated + */ + private static void populateStatementsBetween(Graph.Block block, Statement from, Statement to, boolean isBetween, Collection between, Graph graph) { + for(Statement statement : block.getStatements()) { + if(between.contains(statement)) + // Stop infinite recursion + return; + if(isBetween) { + if(statement.equals(to)) + // The end was reached! + isBetween = false; + else + // We are between - add the statement + between.add(statement); + } else { + if(statement.equals(from)) + // We are now between! + isBetween = true; + } + } + if(isBetween) { + // Recurse to successor blocks + final Collection successors = block.getSuccessors(); + for(LabelRef successor : successors) { + if(successor.getFullName().equals(ProcedureRef.PROCEXIT_BLOCK_NAME)) + continue; + final Graph.Block successorBlock = graph.getBlock(successor); + populateStatementsBetween(successorBlock, from, to, true, between, graph); + } + } + } +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertBlocks.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertBlocks.java index 9ba91ccbe..156e9770c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertBlocks.java @@ -33,7 +33,7 @@ public class Pass2AssertBlocks extends Pass2SsaAssertion { } } - private static class BlockReferenceChecker extends ControlFlowGraphBaseVisitor { + private static class BlockReferenceChecker extends GraphBaseVisitor { Set seenBlocks; private Graph graph; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallLvalues.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallLvalues.java index f04603ac5..407392467 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallLvalues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallLvalues.java @@ -1,6 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.symbols.Procedure; @@ -15,7 +15,7 @@ public class Pass2AssertNoCallLvalues extends Pass2SsaAssertion { @Override public void check() throws AssertionFailed { - ControlFlowGraphBaseVisitor checkCalls = new ControlFlowGraphBaseVisitor() { + GraphBaseVisitor checkCalls = new GraphBaseVisitor() { @Override public Void visitCall(StatementCall call) { Procedure procedure = getScope().getProcedure(call.getProcedure()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallParameters.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallParameters.java index dc1ab5413..911a0e3c4 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallParameters.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoCallParameters.java @@ -1,6 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.symbols.Procedure; @@ -17,7 +17,7 @@ public class Pass2AssertNoCallParameters extends Pass2SsaAssertion { @Override public void check() throws AssertionFailed { - ControlFlowGraphBaseVisitor checkCalls = new ControlFlowGraphBaseVisitor() { + GraphBaseVisitor checkCalls = new GraphBaseVisitor() { @Override public Void visitCall(StatementCall call) { Procedure procedure = getScope().getProcedure(call.getProcedure()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLabels.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLabels.java index be3e1979c..b829e9236 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLabels.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoLabels.java @@ -1,6 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.StatementLabel; @@ -14,7 +14,7 @@ public class Pass2AssertNoLabels extends Pass2SsaAssertion { @Override public void check() throws AssertionFailed { - ControlFlowGraphBaseVisitor checkCalls = new ControlFlowGraphBaseVisitor() { + GraphBaseVisitor checkCalls = new GraphBaseVisitor() { @Override public Void visitJumpTarget(StatementLabel jumpTarget) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoProcs.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoProcs.java index 836b4c25d..faef7e0c0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoProcs.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertNoProcs.java @@ -1,6 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.StatementProcedureBegin; import dk.camelot64.kickc.model.statements.StatementProcedureEnd; @@ -15,7 +15,7 @@ public class Pass2AssertNoProcs extends Pass2SsaAssertion { @Override public void check() throws AssertionFailed { - ControlFlowGraphBaseVisitor checkCalls = new ControlFlowGraphBaseVisitor() { + GraphBaseVisitor checkCalls = new GraphBaseVisitor() { @Override public Void visitProcedureBegin(StatementProcedureBegin procedureBegin) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java index f52ff81a2..0973d0723 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java @@ -120,14 +120,14 @@ public class Pass2ConditionalJumpSimplification extends Pass2SsaOptimization { // Found referenced load/store variables // Examine all statements between the conditionAssignment and conditionalJump for modifications final StatementInfos statementInfos = getProgram().getStatementInfos(); - Collection statementsBetween = getGraph().getStatementsBetween(simpleCondition.conditionAssignment, simpleCondition.conditionalJump, statementInfos); + final Graph.Block conditionDefineBlock = statementInfos.getBlock(simpleCondition.conditionAssignment); + Collection statementsBetween = StatementsBetween.find(simpleCondition.conditionAssignment, conditionDefineBlock, simpleCondition.conditionalJump, getGraph()); for(Statement statementBetween : statementsBetween) { for(Variable referencedLoadStoreVariable : referencedLoadStoreVariables) { if(variableReferenceInfos.getDefinedVars(statementBetween).contains(referencedLoadStoreVariable.getVariableRef())) { // A referenced load/store-variable is modified in a statement between the assignment and the condition! getLog().append("Condition not simple " + simpleCondition.conditionVar.toString(getProgram()) + " " + simpleCondition.conditionalJump.toString(getProgram(), false)); // Create an intermediate variable copy of the load/store-variable at the position of the condition-variable to preserve the value - final Graph.Block conditionDefineBlock = statementInfos.getBlock(simpleCondition.conditionAssignment); final ScopeRef conditionDefineScopeRef = conditionDefineBlock.getScope(); final Scope conditionDefineScope = getProgramScope().getScope(conditionDefineScopeRef); SymbolType typeQualified = referencedLoadStoreVariable.getType().getQualified(false, referencedLoadStoreVariable.getType().isNomodify()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java b/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java index 6c961a814..385e132ae 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2EliminateUnusedBlocks.java @@ -103,8 +103,8 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization { */ private Set getReferencedBlocks() { Set referencedBlocks = new LinkedHashSet<>(); - List entryPointBlocks = getProgram().getEntryPointBlocks(); - for(ControlFlowBlock entryPointBlock : entryPointBlocks) { + List entryPointBlocks = getProgram().getEntryPointBlocks(); + for(var entryPointBlock : entryPointBlocks) { findReferencedBlocks(entryPointBlock, referencedBlocks); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java index c0b73f09f..4a1bf52c0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java @@ -57,7 +57,7 @@ public abstract class Pass2SsaOptimization extends Pass1Base implements PassStep * @param replacements Variables that have alias values. */ public void replaceLabels(Graph.Block block, final Map replacements) { - ControlFlowGraphBaseVisitor visitor = getLabelReplaceVisitor(replacements); + GraphBaseVisitor visitor = getLabelReplaceVisitor(replacements); visitor.visitBlock(block); } @@ -67,13 +67,13 @@ public abstract class Pass2SsaOptimization extends Pass1Base implements PassStep * @param replacements Variables that have alias values. */ public void replaceLabels(Statement statement, final Map replacements) { - ControlFlowGraphBaseVisitor visitor = getLabelReplaceVisitor(replacements); + GraphBaseVisitor visitor = getLabelReplaceVisitor(replacements); visitor.visitStatement(statement); } /** Creates a visitor that can replace labels. */ - private ControlFlowGraphBaseVisitor getLabelReplaceVisitor(final Map replacements) { - return new ControlFlowGraphBaseVisitor() { + private GraphBaseVisitor getLabelReplaceVisitor(final Map replacements) { + return new GraphBaseVisitor() { @Override public Void visitConditionalJump(StatementConditionalJump conditionalJump) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java index f5a7ab586..ab1be6c46 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java @@ -1,6 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.LiveRangeEquivalenceClass; import dk.camelot64.kickc.model.LiveRangeEquivalenceClassSet; import dk.camelot64.kickc.model.Program; @@ -52,7 +52,7 @@ public class Pass3PhiMemCoalesce extends Pass2SsaOptimization { /** * Creates initial live range equivalence classes from program phi statements. */ - public static class EquivalenceClassPhiInitializer extends ControlFlowGraphBaseVisitor { + public static class EquivalenceClassPhiInitializer extends GraphBaseVisitor { private Program program; @@ -99,7 +99,7 @@ public class Pass3PhiMemCoalesce extends Pass2SsaOptimization { } /** Coalesces phi equivalence classes when they do not overlap based on assignments to variables in phi statements. */ - private class PhiMemCoalescer extends ControlFlowGraphBaseVisitor { + private class PhiMemCoalescer extends GraphBaseVisitor { private LiveRangeEquivalenceClassSet phiEquivalenceClassSet; private List remove; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java index 9a7246a7a..ea9828b9e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java @@ -109,7 +109,7 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base { /** * Add all variables to a non-overlapping equivalence or create a new one. */ - private class EquivalenceClassAdder extends ControlFlowGraphBaseVisitor { + private class EquivalenceClassAdder extends GraphBaseVisitor { private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet; diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java b/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java index 1e8d66ebb..00866461b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNBlockSequencePlanner.java @@ -20,9 +20,9 @@ public class PassNBlockSequencePlanner extends Pass2SsaOptimization { @Override public boolean step() { - List entryPointBlocks = getProgram().getEntryPointBlocks(); + List entryPointBlocks = getProgram().getEntryPointBlocks(); - for(ControlFlowBlock entryPointBlock : entryPointBlocks) { + for(var entryPointBlock : entryPointBlocks) { pushTodo(entryPointBlock); } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java index af6d20983..774b325d8 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java @@ -1,7 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor; +import dk.camelot64.kickc.model.GraphBaseVisitor; import dk.camelot64.kickc.model.Graph; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.StatementPhiBlock; @@ -62,7 +61,7 @@ public class PassNCullEmptyBlocks extends Pass2SsaOptimization { continue; // In all phi functions of a successor blocks make a copy of the phi assignment for each predecessor - ControlFlowGraphBaseVisitor phiFixVisitor = new ControlFlowGraphBaseVisitor() { + GraphBaseVisitor phiFixVisitor = new GraphBaseVisitor() { @Override public Void visitPhiBlock(StatementPhiBlock phi) { for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {