1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-29 02:50:11 +00:00

#815 working on generalizing the control flow graph and control flow block into an interface.

This commit is contained in:
jespergravgaard 2023-04-09 00:02:48 +02:00
parent 5a74d14b41
commit 416184894f
16 changed files with 104 additions and 79 deletions

View File

@ -2,7 +2,6 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import java.util.*; 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<Statement> getStatementsBetween(Statement from, Statement to, StatementInfos statementInfos) {
Collection<Statement> 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<Statement> 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<LabelRef> 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);
}
}
}
} }

View File

@ -157,6 +157,22 @@ public interface Graph {
return successors; return successors;
} }
public List<Comment> getComments();
void setComments(List<Comment> 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); String toString(Program program, Graph graph);
} }

View File

@ -5,7 +5,7 @@ import dk.camelot64.kickc.model.statements.*;
import java.util.Collection; import java.util.Collection;
/** Base visitor for iterating through a control flow graph */ /** Base visitor for iterating through a control flow graph */
public class ControlFlowGraphBaseVisitor<T> { public class GraphBaseVisitor<T> {
public T visitGraph(Graph graph) { public T visitGraph(Graph graph) {
Collection<Graph.Block> blocks = graph.getAllBlocks(); Collection<Graph.Block> blocks = graph.getAllBlocks();

View File

@ -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<Statement> find(Statement from, Graph.Block fromBlock, Statement to, Graph graph) {
Collection<Statement> 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<Statement> 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<LabelRef> 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);
}
}
}
}

View File

@ -33,7 +33,7 @@ public class Pass2AssertBlocks extends Pass2SsaAssertion {
} }
} }
private static class BlockReferenceChecker extends ControlFlowGraphBaseVisitor<Void> { private static class BlockReferenceChecker extends GraphBaseVisitor<Void> {
Set<LabelRef> seenBlocks; Set<LabelRef> seenBlocks;
private Graph graph; private Graph graph;

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes; 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.Program;
import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Procedure;
@ -15,7 +15,7 @@ public class Pass2AssertNoCallLvalues extends Pass2SsaAssertion {
@Override @Override
public void check() throws AssertionFailed { public void check() throws AssertionFailed {
ControlFlowGraphBaseVisitor<Void> checkCalls = new ControlFlowGraphBaseVisitor<Void>() { GraphBaseVisitor<Void> checkCalls = new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitCall(StatementCall call) { public Void visitCall(StatementCall call) {
Procedure procedure = getScope().getProcedure(call.getProcedure()); Procedure procedure = getScope().getProcedure(call.getProcedure());

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes; 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.Program;
import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Procedure;
@ -17,7 +17,7 @@ public class Pass2AssertNoCallParameters extends Pass2SsaAssertion {
@Override @Override
public void check() throws AssertionFailed { public void check() throws AssertionFailed {
ControlFlowGraphBaseVisitor<Void> checkCalls = new ControlFlowGraphBaseVisitor<Void>() { GraphBaseVisitor<Void> checkCalls = new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitCall(StatementCall call) { public Void visitCall(StatementCall call) {
Procedure procedure = getScope().getProcedure(call.getProcedure()); Procedure procedure = getScope().getProcedure(call.getProcedure());

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes; 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.Program;
import dk.camelot64.kickc.model.statements.StatementLabel; import dk.camelot64.kickc.model.statements.StatementLabel;
@ -14,7 +14,7 @@ public class Pass2AssertNoLabels extends Pass2SsaAssertion {
@Override @Override
public void check() throws AssertionFailed { public void check() throws AssertionFailed {
ControlFlowGraphBaseVisitor<Void> checkCalls = new ControlFlowGraphBaseVisitor<Void>() { GraphBaseVisitor<Void> checkCalls = new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitJumpTarget(StatementLabel jumpTarget) { public Void visitJumpTarget(StatementLabel jumpTarget) {

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes; 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.Program;
import dk.camelot64.kickc.model.statements.StatementProcedureBegin; import dk.camelot64.kickc.model.statements.StatementProcedureBegin;
import dk.camelot64.kickc.model.statements.StatementProcedureEnd; import dk.camelot64.kickc.model.statements.StatementProcedureEnd;
@ -15,7 +15,7 @@ public class Pass2AssertNoProcs extends Pass2SsaAssertion {
@Override @Override
public void check() throws AssertionFailed { public void check() throws AssertionFailed {
ControlFlowGraphBaseVisitor<Void> checkCalls = new ControlFlowGraphBaseVisitor<Void>() { GraphBaseVisitor<Void> checkCalls = new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitProcedureBegin(StatementProcedureBegin procedureBegin) { public Void visitProcedureBegin(StatementProcedureBegin procedureBegin) {

View File

@ -120,14 +120,14 @@ public class Pass2ConditionalJumpSimplification extends Pass2SsaOptimization {
// Found referenced load/store variables // Found referenced load/store variables
// Examine all statements between the conditionAssignment and conditionalJump for modifications // Examine all statements between the conditionAssignment and conditionalJump for modifications
final StatementInfos statementInfos = getProgram().getStatementInfos(); final StatementInfos statementInfos = getProgram().getStatementInfos();
Collection<Statement> statementsBetween = getGraph().getStatementsBetween(simpleCondition.conditionAssignment, simpleCondition.conditionalJump, statementInfos); final Graph.Block conditionDefineBlock = statementInfos.getBlock(simpleCondition.conditionAssignment);
Collection<Statement> statementsBetween = StatementsBetween.find(simpleCondition.conditionAssignment, conditionDefineBlock, simpleCondition.conditionalJump, getGraph());
for(Statement statementBetween : statementsBetween) { for(Statement statementBetween : statementsBetween) {
for(Variable referencedLoadStoreVariable : referencedLoadStoreVariables) { for(Variable referencedLoadStoreVariable : referencedLoadStoreVariables) {
if(variableReferenceInfos.getDefinedVars(statementBetween).contains(referencedLoadStoreVariable.getVariableRef())) { if(variableReferenceInfos.getDefinedVars(statementBetween).contains(referencedLoadStoreVariable.getVariableRef())) {
// A referenced load/store-variable is modified in a statement between the assignment and the condition! // 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)); 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 // 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 ScopeRef conditionDefineScopeRef = conditionDefineBlock.getScope();
final Scope conditionDefineScope = getProgramScope().getScope(conditionDefineScopeRef); final Scope conditionDefineScope = getProgramScope().getScope(conditionDefineScopeRef);
SymbolType typeQualified = referencedLoadStoreVariable.getType().getQualified(false, referencedLoadStoreVariable.getType().isNomodify()); SymbolType typeQualified = referencedLoadStoreVariable.getType().getQualified(false, referencedLoadStoreVariable.getType().isNomodify());

View File

@ -103,8 +103,8 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization {
*/ */
private Set<LabelRef> getReferencedBlocks() { private Set<LabelRef> getReferencedBlocks() {
Set<LabelRef> referencedBlocks = new LinkedHashSet<>(); Set<LabelRef> referencedBlocks = new LinkedHashSet<>();
List<ControlFlowBlock> entryPointBlocks = getProgram().getEntryPointBlocks(); List<Graph.Block> entryPointBlocks = getProgram().getEntryPointBlocks();
for(ControlFlowBlock entryPointBlock : entryPointBlocks) { for(var entryPointBlock : entryPointBlocks) {
findReferencedBlocks(entryPointBlock, referencedBlocks); findReferencedBlocks(entryPointBlock, referencedBlocks);
} }

View File

@ -57,7 +57,7 @@ public abstract class Pass2SsaOptimization extends Pass1Base implements PassStep
* @param replacements Variables that have alias values. * @param replacements Variables that have alias values.
*/ */
public void replaceLabels(Graph.Block block, final Map<LabelRef, LabelRef> replacements) { public void replaceLabels(Graph.Block block, final Map<LabelRef, LabelRef> replacements) {
ControlFlowGraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements); GraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements);
visitor.visitBlock(block); visitor.visitBlock(block);
} }
@ -67,13 +67,13 @@ public abstract class Pass2SsaOptimization extends Pass1Base implements PassStep
* @param replacements Variables that have alias values. * @param replacements Variables that have alias values.
*/ */
public void replaceLabels(Statement statement, final Map<LabelRef, LabelRef> replacements) { public void replaceLabels(Statement statement, final Map<LabelRef, LabelRef> replacements) {
ControlFlowGraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements); GraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements);
visitor.visitStatement(statement); visitor.visitStatement(statement);
} }
/** Creates a visitor that can replace labels. */ /** Creates a visitor that can replace labels. */
private ControlFlowGraphBaseVisitor<Void> getLabelReplaceVisitor(final Map<LabelRef, LabelRef> replacements) { private GraphBaseVisitor<Void> getLabelReplaceVisitor(final Map<LabelRef, LabelRef> replacements) {
return new ControlFlowGraphBaseVisitor<Void>() { return new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitConditionalJump(StatementConditionalJump conditionalJump) { public Void visitConditionalJump(StatementConditionalJump conditionalJump) {

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes; 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.LiveRangeEquivalenceClass;
import dk.camelot64.kickc.model.LiveRangeEquivalenceClassSet; import dk.camelot64.kickc.model.LiveRangeEquivalenceClassSet;
import dk.camelot64.kickc.model.Program; 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. * Creates initial live range equivalence classes from program phi statements.
*/ */
public static class EquivalenceClassPhiInitializer extends ControlFlowGraphBaseVisitor<Void> { public static class EquivalenceClassPhiInitializer extends GraphBaseVisitor<Void> {
private Program program; 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. */ /** Coalesces phi equivalence classes when they do not overlap based on assignments to variables in phi statements. */
private class PhiMemCoalescer extends ControlFlowGraphBaseVisitor<Void> { private class PhiMemCoalescer extends GraphBaseVisitor<Void> {
private LiveRangeEquivalenceClassSet phiEquivalenceClassSet; private LiveRangeEquivalenceClassSet phiEquivalenceClassSet;
private List<VariableRef> remove; private List<VariableRef> remove;

View File

@ -109,7 +109,7 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
/** /**
* Add all variables to a non-overlapping equivalence or create a new one. * Add all variables to a non-overlapping equivalence or create a new one.
*/ */
private class EquivalenceClassAdder extends ControlFlowGraphBaseVisitor<Void> { private class EquivalenceClassAdder extends GraphBaseVisitor<Void> {
private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet; private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet;

View File

@ -20,9 +20,9 @@ public class PassNBlockSequencePlanner extends Pass2SsaOptimization {
@Override @Override
public boolean step() { public boolean step() {
List<ControlFlowBlock> entryPointBlocks = getProgram().getEntryPointBlocks(); List<Graph.Block> entryPointBlocks = getProgram().getEntryPointBlocks();
for(ControlFlowBlock entryPointBlock : entryPointBlocks) { for(var entryPointBlock : entryPointBlocks) {
pushTodo(entryPointBlock); pushTodo(entryPointBlock);
} }

View File

@ -1,7 +1,6 @@
package dk.camelot64.kickc.passes; package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.GraphBaseVisitor;
import dk.camelot64.kickc.model.ControlFlowGraphBaseVisitor;
import dk.camelot64.kickc.model.Graph; import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.statements.StatementPhiBlock;
@ -62,7 +61,7 @@ public class PassNCullEmptyBlocks extends Pass2SsaOptimization {
continue; continue;
// In all phi functions of a successor blocks make a copy of the phi assignment for each predecessor // In all phi functions of a successor blocks make a copy of the phi assignment for each predecessor
ControlFlowGraphBaseVisitor<Void> phiFixVisitor = new ControlFlowGraphBaseVisitor<Void>() { GraphBaseVisitor<Void> phiFixVisitor = new GraphBaseVisitor<Void>() {
@Override @Override
public Void visitPhiBlock(StatementPhiBlock phi) { public Void visitPhiBlock(StatementPhiBlock phi) {
for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) { for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {