1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-01 00:51:12 +00:00

#815 working on moving control flow graphs into procedure compilation.

This commit is contained in:
jespergravgaard 2023-04-09 22:49:53 +02:00
parent 5905c1d5b6
commit 1fad48f8bc
15 changed files with 214 additions and 173 deletions

View File

@ -11,19 +11,19 @@ import java.util.*;
*/
public class ControlFlowGraph implements Graph {
private List<ControlFlowBlock> blocks;
private List<Graph.Block> blocks;
/**
* Sequence of blocks used when generating ASM
*/
private List<LabelRef> sequence;
public ControlFlowGraph(List<ControlFlowBlock> blocks) {
public ControlFlowGraph(List<Graph.Block> 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<ControlFlowBlock> blocks) {
this.blocks = blocks;
}
public void remove(LabelRef label) {
ListIterator<ControlFlowBlock> blocksIt = blocks.listIterator();
ListIterator<Graph.Block> 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<LabelRef> getSequence() {
return sequence;
}
public void setSequence(List<LabelRef> 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<ControlFlowBlock> 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);
}

View File

@ -16,6 +16,8 @@ public interface Graph {
List<Block> getAllBlocks();
void addBlock(Graph.Block block);
default List<Graph.Block> getPredecessors(Graph.Block block) {
ArrayList<Block> predecessorBlocks = new ArrayList<>();
for(Graph.Block other : getAllBlocks()) {

View File

@ -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<Block> 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<ProcedureCompilation> getProcedureCompilations() {
public Collection<ProcedureCompilation> getProcedureCompilations() {
return procedureCompilations.values();
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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<LabelRef> visited) {
public void assertReturn(ControlFlowGraph graph, Graph.Block block, Collection<LabelRef> 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 {

View File

@ -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<LabelRef, LabelRef> splitBlockMap = new LinkedHashMap<>();
public void execute() {
final List<Graph.Block> todoBlocks = getGraph().getAllBlocks();
for(ProcedureCompilation procedureCompilation : this.program.getProcedureCompilations()) {
handleProcedure(procedureCompilation);
}
}
private void handleProcedure(ProcedureCompilation procedureCompilation) {
final Graph graph = procedureCompilation.getGraph();
final List<Graph.Block> todoBlocks = new ArrayList<>(graph.getAllBlocks());
List<Graph.Block> doneBlocks = new ArrayList<>();
Map<LabelRef, LabelRef> 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<Statement> 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<Statement> stmtIt, Graph.Block block) {
private ControlFlowBlock handlePhiCall(StatementCall call, Procedure procedure, ListIterator<Statement> stmtIt, Graph.Block block, Map<LabelRef, LabelRef> splitBlockMap) {
List<Variable> parameterDefs = procedure.getParameters();
List<RValue> parameterValues = call.getParameters();

View File

@ -32,10 +32,10 @@ public class Pass1GenerateControlFlowGraph extends Pass1Base {
// Empty procedures should not produce any blocks
continue;
List<ControlFlowBlock> blocks = new ArrayList<>();
List<Graph.Block> 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<ControlFlowBlock> blocks) {
for(ControlFlowBlock block : blocks) {
private Graph.Block getOrCreateBlock(LabelRef label, ScopeRef scope, List<Graph.Block> 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;
}

View File

@ -23,16 +23,23 @@ public class Pass1ProcedureInline extends Pass1Base {
@Override
public boolean step() {
List<Graph.Block> allBlocks = getGraph().getAllBlocks();
ListIterator<Graph.Block> 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<Graph.Block> procedureBlocks = new ArrayList<>(procedureGraph.getAllBlocks());
ListIterator<Graph.Block> blocksIt = procedureBlocks.listIterator();
while(blocksIt.hasNext()) {
Graph.Block block = blocksIt.next();
List<Statement> blockStatements = block.getStatements();
ListIterator<Statement> 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<Statement> 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);

View File

@ -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<VariableRef> 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<LabelRef, LabelRef> 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;
}

View File

@ -71,7 +71,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization {
private void removeParameters(Set<ConstantRef> 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<Variable> 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
*

View File

@ -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<LabelRef> 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<LabelRef> removedBlocks, Program program) {
program.getLog().append("Removing unused procedure " + procedureRef);
Procedure unusedProcedure = program.getScope().getProcedure(procedureRef);
List<Graph.Block> procedureBlocks = program.getGraph().getScopeBlocks(procedureRef);
final ProcedureCompilation procedureCompilation = program.getProcedureCompilation(procedureRef);
final ControlFlowGraph procedureGraph = procedureCompilation.getGraph();
List<Graph.Block> 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<LabelRef> getReferencedBlocks() {
Set<LabelRef> referencedBlocks = new LinkedHashSet<>();
List<Graph.Block> 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<LabelRef> getReferencedBlocks() {
Set<LabelRef> referencedBlocks = new LinkedHashSet<>();
List<Graph.Block> entryPointBlocks = getProgram().getEntryPointBlocks();
for(var entryPointBlock : entryPointBlocks) {
findReferencedBlocks(entryPointBlock, referencedBlocks);
}
return referencedBlocks;
}
/**
* Find all referenced block labels recursively
*

View File

@ -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<Graph.Block> liftedBlocks = new ArrayList<>(graph.getAllBlocks());
ProgramScope programScope = program.getScope();
List<Graph.Block> blocks = graph.getAllBlocks();
ListIterator<Graph.Block> blocksIt = blocks.listIterator();
ListIterator<Graph.Block> 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<Statement> 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<Graph.Block> blocks, LabelRef blockRef) {
return blocks.stream().filter(block -> block.getLabel().equals(blockRef)).findFirst().orElse(null);
}
}

View File

@ -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<LabelRef> 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<LabelRef> procedureLabels = procedureGraph.getAllBlocks().stream().map(Graph.Block::getLabel).toList();
final List<Graph.Block> 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();

View File

@ -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<Graph.Block> 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()));
}