1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 02:54:41 +00:00

Created "loop-unroll" branch.

This commit is contained in:
jespergravgaard 2018-08-19 21:48:26 +02:00
parent a87da922f8
commit b19bf2246e
25 changed files with 1030 additions and 615 deletions

View File

@ -30,12 +30,12 @@ public class CompileLog {
/**
* Should SSA optimization be verbose.
*/
private boolean verboseSSAOptimize = false;
private boolean verboseSSAOptimize = true;
/**
* Should the log be output to System.out while being built
*/
private boolean sysOut = false;
private boolean sysOut = true;
public CompileLog() {
this.log = new StringBuilder();

View File

@ -94,6 +94,8 @@ public class Compiler {
pass1GenerateSSA();
pass2OptimizeSSA();
pass2UnrollLoops();
pass2InlineConstants();
pass3Analysis();
pass4RegisterAllocation();
pass5GenerateAndOptimizeAsm();
@ -201,9 +203,30 @@ public class Compiler {
optimizations.add(new Pass2NopCastElimination(program));
optimizations.add(new Pass2EliminateUnusedBlocks(program));
optimizations.add(new Pass2RangeResolving(program));
pass2OptimizeSSA(optimizations);
}
private void pass2UnrollLoops() {
getLog().append("CONTROL FLOW GRAPH BEFORE UNROLLING");
getLog().append(program.getGraph().toString(program));
new Pass2DominatorsAnalysis(program).step();
getLog().append("NATURAL LOOPS");
new Pass2LoopAnalysis(program).step();
getLog().append(program.getLoopSet().toString());
List<Pass2SsaOptimization> loopUnrolling = new ArrayList<>();
loopUnrolling.add(new PassNStatementIndices(program));
loopUnrolling.add(new PassNVariableReferenceInfos(program));
loopUnrolling.add(new Pass3StatementInfos(program));
loopUnrolling.add(new Pass2DominatorsAnalysis(program));
loopUnrolling.add(new Pass2LoopAnalysis(program));
loopUnrolling.add(new Pass2LoopUnroll(program));
pass2OptimizeSSA(loopUnrolling);
}
private void pass2InlineConstants() {
// Constant inlining optimizations - as the last step to ensure that constant identification has been completed
List<Pass2SsaOptimization> constantOptimizations = new ArrayList<>();
constantOptimizations.add(new Pass2ConstantInlining(program));
@ -212,7 +235,6 @@ public class Compiler {
constantOptimizations.add(new Pass2ConstantAdditionElimination(program));
constantOptimizations.add(new Pass2ConstantIfs(program));
pass2OptimizeSSA(constantOptimizations);
}
private void pass2OptimizeSSA(List<Pass2SsaOptimization> optimizations) {
@ -252,7 +274,7 @@ public class Compiler {
//getLog().append(program.getGraph().toString(program));
pass2AssertSSA();
new Pass3AddNopBeforeCallOns(program).generate();
new PassNStatementIndices(program).generateStatementIndices();
new PassNStatementIndices(program).execute();
getLog().append("CALL GRAPH");
new Pass3CallGraphAnalysis(program).findCallGraph();
@ -260,8 +282,8 @@ public class Compiler {
//getLog().setVerboseLiveRanges(true);
new Pass3StatementInfos(program).generateStatementInfos();
new PassNVariableReferenceInfos(program).generateVariableReferenceInfos();
new Pass3StatementInfos(program).execute();
new PassNVariableReferenceInfos(program).execute();
new Pass3LiveRangesAnalysis(program).findLiveRanges();
//getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND");
//getLog().append(program.getGraph().toString(program));
@ -274,10 +296,10 @@ public class Compiler {
new Pass2CullEmptyBlocks(program).step();
new Pass3BlockSequencePlanner(program).plan();
new Pass3AddNopBeforeCallOns(program).generate();
new PassNStatementIndices(program).generateStatementIndices();
new PassNStatementIndices(program).execute();
new Pass3CallGraphAnalysis(program).findCallGraph();
new Pass3StatementInfos(program).generateStatementInfos();
new PassNVariableReferenceInfos(program).generateVariableReferenceInfos();
new Pass3StatementInfos(program).execute();
new PassNVariableReferenceInfos(program).execute();
new Pass3SymbolInfos(program).generateSymbolInfos();
new Pass3LiveRangesAnalysis(program).findLiveRanges();
//getLog().append("CONTROL FLOW GRAPH - BEFORE EFFECTIVE LIVE RANGES");
@ -294,11 +316,11 @@ public class Compiler {
private void pass4RegisterAllocation() {
getLog().append("DOMINATORS");
new Pass3DominatorsAnalysis(program).findDominators();
new Pass2DominatorsAnalysis(program).step();
getLog().append(program.getDominators().toString());
getLog().append("NATURAL LOOPS");
new Pass3LoopAnalysis(program).findLoops();
new Pass2LoopAnalysis(program).step();
getLog().append(program.getLoopSet().toString());
getLog().append("NATURAL LOOPS WITH DEPTH");

View File

@ -151,7 +151,9 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
Operator operator = origConditionalJump.getOperator();
RValue rValue2 = origConditionalJump.getrValue2();
LabelRef destination = origConditionalJump.getDestination();
return new StatementConditionalJump(rValue1, operator, rValue2, destination, origConditionalJump.getSource());
StatementConditionalJump conditionalJump = new StatementConditionalJump(rValue1, operator, rValue2, destination, origConditionalJump.getSource());
conditionalJump.setDeclaredUnroll(origConditionalJump.isDeclaredUnroll());
return conditionalJump;
}
@Override

View File

@ -1,13 +1,14 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.passes.Pass2LoopAnalysis;
import java.util.*;
/**
* A set of natural loops in a control flow graph.
* <p>For definitions and more see http://www.cs.colostate.edu/~cs553/ClassNotes/lecture09-control-dominators.ppt.pdf
* <p>Created by {@link dk.camelot64.kickc.passes.Pass3LoopAnalysis}
* <p>Created by {@link Pass2LoopAnalysis}
*/
public class NaturalLoopSet {

View File

@ -230,6 +230,23 @@ public class VariableReferenceInfos {
return stmts;
}
/**
* Get all statements referencing a variable
*
* @param varRef The variable to look for
* @return Index of all statements referencing the constant
*/
public Collection<Integer> getVarRefStatements(VariableRef varRef) {
Collection<ReferenceToSymbolVar> refs = symbolVarReferences.get(varRef);
LinkedHashSet<Integer> stmts = new LinkedHashSet<>();
refs.stream()
.filter(referenceToSymbolVar -> referenceToSymbolVar instanceof ReferenceInStatement)
.forEach(referenceToSymbolVar -> stmts.add(((ReferenceInStatement) referenceToSymbolVar).getStatementIdx()));
return stmts;
}
/**
* Get all constatns referencing another constant
*

View File

@ -6,8 +6,7 @@ import dk.camelot64.kickc.model.operators.Operator;
import dk.camelot64.kickc.model.values.RValue;
/**
* Intermediate Compiler Form Statement with a conditional jump.
* Intermediate form used for compiler optimization.
* SSA form conditional jump. Intermediate form used for compiler optimization.
* <br>
* <i> if ( Y<sub>j</sub> ) goto XX </i>
* <br>
@ -19,6 +18,7 @@ public class StatementConditionalJump extends StatementBase {
private Operator operator;
private RValue rValue2;
private LabelRef destination;
private boolean declaredUnroll;
public StatementConditionalJump(RValue condition, LabelRef destination,StatementSource source) {
super(null, source);
@ -34,17 +34,7 @@ public class StatementConditionalJump extends StatementBase {
RValue rValue2,
LabelRef destination,
StatementSource source) {
this(rValue1, operator, rValue2, destination, null, source);
}
public StatementConditionalJump(
RValue rValue1,
Operator operator,
RValue rValue2,
LabelRef destination,
Integer index,
StatementSource source) {
super(index, source);
super(null, source);
this.rValue1 = rValue1;
this.operator = operator;
this.rValue2 = rValue2;
@ -83,10 +73,22 @@ public class StatementConditionalJump extends StatementBase {
this.destination = destination;
}
public boolean isDeclaredUnroll() {
return declaredUnroll;
}
public void setDeclaredUnroll(boolean declaredUnroll) {
this.declaredUnroll = declaredUnroll;
}
@Override
public String toString(Program program, boolean aliveInfo) {
StringBuilder out = new StringBuilder();
out.append(super.idxString());
if(declaredUnroll) {
out.append("unroll ");
}
out.append("if(");
if(rValue1 != null) {
out.append(rValue1.toString(program));

View File

@ -68,7 +68,7 @@ public class StatementInfos {
/**
* Get statement from index
*
* @param Statement index
* @param index Statement index
* @return The statement with the passed index
*/
public Statement getStatement(int index) {

View File

@ -54,7 +54,7 @@ public class StatementPhiBlock extends StatementBase {
return getPhiVariable(variable).getrValue(predecessor);
}
private PhiVariable getPhiVariable(VariableRef variable) {
public PhiVariable getPhiVariable(VariableRef variable) {
for(PhiVariable phiVariable : phiVariables) {
if(phiVariable.getVariable().equals(variable)) {
return phiVariable;

View File

@ -77,9 +77,9 @@ stmt
| '{' stmtSeq? '}' #stmtBlock
| expr ';' #stmtExpr
| 'if' '(' expr ')' stmt ( 'else' stmt )? #stmtIfElse
| directive? 'while' '(' expr ')' stmt #stmtWhile
| directive? 'do' stmt 'while' '(' expr ')' ';' #stmtDoWhile
| directive? 'for' '(' forDeclaration? forIteration ')' stmt #stmtFor
| directive* 'while' '(' expr ')' stmt #stmtWhile
| directive* 'do' stmt 'while' '(' expr ')' ';' #stmtDoWhile
| directive* 'for' '(' forDeclaration? forIteration ')' stmt #stmtFor
| 'return' expr? ';' #stmtReturn
| 'asm' '{' asmLines '}' #stmtAsm
| declKasm #stmtDeclKasm

File diff suppressed because it is too large Load Diff

View File

@ -359,6 +359,28 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
/**
* Add declared directives to a conditional jump (as part of a loop).
*
* @param conditional The loop conditional
* @param directivesCtx The directives to add
*/
private void addDirectives(StatementConditionalJump conditional, List<KickCParser.DirectiveContext> directivesCtx) {
List<Directive> directives = new ArrayList<>();
for(KickCParser.DirectiveContext directiveContext : directivesCtx) {
directives.add((Directive) this.visit(directiveContext));
}
for(Directive directive : directives) {
StatementSource source = new StatementSource(directivesCtx.get(0));
if(directive instanceof DirectiveInline) {
conditional.setDeclaredUnroll(true);
} else {
throw new CompileError("Unsupported loop directive " + directive, source);
}
}
}
@Override
public Directive visitDirectiveConst(KickCParser.DirectiveConstContext ctx) {
return new DirectiveConst();
@ -466,7 +488,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
Statement doJmpStmt = new StatementConditionalJump(rValue, doJumpLabel.getRef(), new StatementSource(ctx));
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, doJumpLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(doJmpStmt);
Statement endJmpStmt = new StatementJump(endJumpLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(endJmpStmt);
@ -477,6 +499,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
sequence.addStatement(beginJmpStmt);
StatementLabel endJumpTarget = new StatementLabel(endJumpLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(endJumpTarget);
// Add directives
addDirectives(doJmpStmt, ctx.directive());
return null;
}
@ -491,8 +516,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
Statement doJmpStmt = new StatementConditionalJump(rValue, beginJumpLabel.getRef(), new StatementSource(ctx));
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, beginJumpLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(doJmpStmt);
addDirectives(doJmpStmt, ctx.directive());
return null;
}
@ -536,8 +564,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
RValue rValue = (RValue) this.visit(ctx.expr(0));
PrePostModifierHandler.addPostModifiers(this, ctx.expr(0));
// Add jump if condition was met
Statement doJmpStmt = new StatementConditionalJump(rValue, repeatLabel.getRef(), new StatementSource(ctx));
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, repeatLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(doJmpStmt);
addDirectives(doJmpStmt, stmtForCtx.directive());
return null;
}
@ -572,8 +601,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
Statement stmtTmpVar = new StatementAssignment(tmpVarRef, lValue.getRef(), Operators.NEQ, beyondLastVal, new StatementSource(ctx));
sequence.addStatement(stmtTmpVar);
// Add jump if condition was met
Statement doJmpStmt = new StatementConditionalJump(tmpVarRef, repeatLabel.getRef(), new StatementSource(ctx));
StatementConditionalJump doJmpStmt = new StatementConditionalJump(tmpVarRef, repeatLabel.getRef(), new StatementSource(ctx));
sequence.addStatement(doJmpStmt);
addDirectives(doJmpStmt, stmtForCtx.directive());
return null;
}

View File

@ -24,8 +24,8 @@ public class Pass1AssertUsedVars extends Pass1Base {
@Override
public boolean step() {
new PassNStatementIndices(getProgram()).generateStatementIndices();
new PassNVariableReferenceInfos(getProgram()).generateVariableReferenceInfos();
new PassNStatementIndices(getProgram()).execute();
new PassNVariableReferenceInfos(getProgram()).execute();
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME));

View File

@ -40,82 +40,7 @@ public class Pass1ProcedureInline extends Pass1Base {
if(procedure.getInterruptType()!=null) {
throw new CompileError("Error! Interrupts cannot be inlined. "+procedure.getRef().toString());
}
Scope callScope = getScope().getScope(block.getScope());
// Remove call
statementsIt.remove();
// Find call serial number (handles when multiple calls to the same procedure is made in the call scope)
int serial = nextSerial(procedure, callScope);
// Copy all procedure symbols
inlineSymbols(procedure, callScope, serial);
// Generate parameter assignments
inlineParameterAssignments(statementsIt, call, procedure, callScope, serial);
// Create a new block label for the rest of the calling block
Label restBlockLabel = callScope.addLabelIntermediate();
// Copy all procedure blocks
List<ControlFlowBlock> procedureBlocks = getGraph().getScopeBlocks(procedure.getRef());
for(ControlFlowBlock procedureBlock : procedureBlocks) {
LabelRef procBlockLabelRef = procedureBlock.getLabel();
Symbol procBlockLabel = getScope().getSymbol(procBlockLabelRef);
Label inlinedBlockLabel;
if(procedure.equals(procBlockLabel)) {
inlinedBlockLabel = callScope.getLabel(procedure.getLocalName() + serial);
} else {
String inlinedBlockLabelName = getInlineSymbolName(procedure, procBlockLabel, serial);
inlinedBlockLabel = callScope.getLabel(inlinedBlockLabelName);
}
ControlFlowBlock inlineBlock = new ControlFlowBlock(inlinedBlockLabel.getRef(), callScope.getRef());
blocksIt.add(inlineBlock);
for(Statement procStatement : procedureBlock.getStatements()) {
Statement inlinedStatement = inlineStatement(procStatement, procedure, callScope, serial);
if(inlinedStatement != null) {
inlineBlock.addStatement(inlinedStatement);
}
}
// Set successors
if(procedureBlock.getDefaultSuccessor() != null) {
LabelRef procBlockSuccessorRef = procedureBlock.getDefaultSuccessor();
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
inlineBlock.setDefaultSuccessor(inlinedSuccessor);
}
if(procedureBlock.getConditionalSuccessor() != null) {
LabelRef procBlockSuccessorRef = procedureBlock.getConditionalSuccessor();
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
inlineBlock.setConditionalSuccessor(inlinedSuccessor);
}
}
// Create a new block for the rest of the calling block
ControlFlowBlock restBlock = new ControlFlowBlock(restBlockLabel.getRef(), callScope.getRef());
blocksIt.add(restBlock);
// Generate return assignment
if(!procedure.getReturnType().equals(SymbolType.VOID)) {
Variable procReturnVar = procedure.getVariable("return");
String inlinedReturnVarName = getInlineSymbolName(procedure, procReturnVar, serial);
Variable inlinedReturnVar = callScope.getVariable(inlinedReturnVarName);
restBlock.addStatement(new StatementAssignment(call.getlValue(), inlinedReturnVar.getRef(), call.getSource()));
} else {
// Remove the tmp var receiving the result
LValue lValue = call.getlValue();
if(lValue instanceof VariableRef) {
callScope.remove(getScope().getVariable((VariableRef) lValue));
call.setlValue(null);
}
}
// Copy the rest of the calling block to the new block
while(statementsIt.hasNext()) {
Statement restStatement = statementsIt.next();
statementsIt.remove();
restBlock.addStatement(restStatement);
}
// Set the successors for the rest block
restBlock.setDefaultSuccessor(block.getDefaultSuccessor());
restBlock.setConditionalSuccessor(block.getConditionalSuccessor());
// Set default successor to the original block to the inlined procedure block
Label inlinedProcLabel = callScope.getLabel(procedure.getLocalName() + serial);
block.setDefaultSuccessor(inlinedProcLabel.getRef());
// Set conditional successor of original block to null (as any condition has been moved to the rest block)
block.setConditionalSuccessor(null);
// Log the inlining
getLog().append("Inlined call " + call.toString(getProgram(), false));
inlineProcedureCall(call, procedure, statementsIt, block, blocksIt);
// Exit and restart
return true;
}
@ -125,6 +50,94 @@ public class Pass1ProcedureInline extends Pass1Base {
return false;
}
/**
* Inline a specific call to a procedure.
*
* @param call The call to inline
* @param procedure The procedure being called
* @param statementsIt The statement iterator pointing to the call statement
* @param block The block containing the call
* @param blocksIt The block iterator pointing to the block containing the call
*/
private void inlineProcedureCall(StatementCall call, Procedure procedure, ListIterator<Statement> statementsIt, ControlFlowBlock block, ListIterator<ControlFlowBlock> blocksIt) {
Scope callScope = getScope().getScope(block.getScope());
// Remove call
statementsIt.remove();
// Find call serial number (handles when multiple calls to the same procedure is made in the call scope)
int serial = nextSerial(procedure, callScope);
// Copy all procedure symbols
inlineSymbols(procedure, callScope, serial);
// Generate parameter assignments
inlineParameterAssignments(statementsIt, call, procedure, callScope, serial);
// Create a new block label for the rest of the calling block
Label restBlockLabel = callScope.addLabelIntermediate();
// Copy all procedure blocks
List<ControlFlowBlock> procedureBlocks = getGraph().getScopeBlocks(procedure.getRef());
for(ControlFlowBlock procedureBlock : procedureBlocks) {
LabelRef procBlockLabelRef = procedureBlock.getLabel();
Symbol procBlockLabel = getScope().getSymbol(procBlockLabelRef);
Label inlinedBlockLabel;
if(procedure.equals(procBlockLabel)) {
inlinedBlockLabel = callScope.getLabel(procedure.getLocalName() + serial);
} else {
String inlinedBlockLabelName = getInlineSymbolName(procedure, procBlockLabel, serial);
inlinedBlockLabel = callScope.getLabel(inlinedBlockLabelName);
}
ControlFlowBlock inlineBlock = new ControlFlowBlock(inlinedBlockLabel.getRef(), callScope.getRef());
blocksIt.add(inlineBlock);
for(Statement procStatement : procedureBlock.getStatements()) {
Statement inlinedStatement = inlineStatement(procStatement, procedure, callScope, serial);
if(inlinedStatement != null) {
inlineBlock.addStatement(inlinedStatement);
}
}
// Set successors
if(procedureBlock.getDefaultSuccessor() != null) {
LabelRef procBlockSuccessorRef = procedureBlock.getDefaultSuccessor();
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
inlineBlock.setDefaultSuccessor(inlinedSuccessor);
}
if(procedureBlock.getConditionalSuccessor() != null) {
LabelRef procBlockSuccessorRef = procedureBlock.getConditionalSuccessor();
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
inlineBlock.setConditionalSuccessor(inlinedSuccessor);
}
}
// Create a new block for the rest of the calling block
ControlFlowBlock restBlock = new ControlFlowBlock(restBlockLabel.getRef(), callScope.getRef());
blocksIt.add(restBlock);
// Generate return assignment
if(!procedure.getReturnType().equals(SymbolType.VOID)) {
Variable procReturnVar = procedure.getVariable("return");
String inlinedReturnVarName = getInlineSymbolName(procedure, procReturnVar, serial);
Variable inlinedReturnVar = callScope.getVariable(inlinedReturnVarName);
restBlock.addStatement(new StatementAssignment(call.getlValue(), inlinedReturnVar.getRef(), call.getSource()));
} else {
// Remove the tmp var receiving the result
LValue lValue = call.getlValue();
if(lValue instanceof VariableRef) {
callScope.remove(getScope().getVariable((VariableRef) lValue));
call.setlValue(null);
}
}
// Copy the rest of the calling block to the new block
while(statementsIt.hasNext()) {
Statement restStatement = statementsIt.next();
statementsIt.remove();
restBlock.addStatement(restStatement);
}
// Set the successors for the rest block
restBlock.setDefaultSuccessor(block.getDefaultSuccessor());
restBlock.setConditionalSuccessor(block.getConditionalSuccessor());
// Set default successor to the original block to the inlined procedure block
Label inlinedProcLabel = callScope.getLabel(procedure.getLocalName() + serial);
block.setDefaultSuccessor(inlinedProcLabel.getRef());
// Set conditional successor of original block to null (as any condition has been moved to the rest block)
block.setConditionalSuccessor(null);
// Log the inlining
getLog().append("Inlined call " + call.toString(getProgram(), false));
}
private LabelRef inlineSuccessor(LabelRef procBlockSuccessorRef, Procedure procedure, Scope callScope, int serial, Label restBlockLabel) {
LabelRef inlinedSuccessor;
if(procBlockSuccessorRef.getFullName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) {
@ -188,7 +201,9 @@ public class Pass1ProcedureInline extends Pass1Base {
String inlineSymbolName = getInlineSymbolName(procedure, procDestination, serial);
inlinedDest = callScope.getLabel(inlineSymbolName);
}
inlinedStatement = new StatementConditionalJump(procConditional.getrValue1(), procConditional.getOperator(), procConditional.getrValue2(), inlinedDest.getRef(), procConditional.getSource());
StatementConditionalJump inlinedConditionalJump = new StatementConditionalJump(procConditional.getrValue1(), procConditional.getOperator(), procConditional.getrValue2(), inlinedDest.getRef(), procConditional.getSource());
inlinedConditionalJump.setDeclaredUnroll(procConditional.isDeclaredUnroll());
inlinedStatement = inlinedConditionalJump;
} else if(procStatement instanceof StatementReturn) {
// No statement needed
return null;

View File

@ -89,7 +89,9 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization {
ControlFlowBlock newBlock = new ControlFlowBlock(newBlockLabel.getRef(), currentScopeRef);
getGraph().addBlock(newBlock);
LabelRef destLabel = conditional.getDestination();
newBlock.getStatements().add(new StatementConditionalJump(conditionAssignment.getrValue2(), destLabel, conditional.getSource()));
StatementConditionalJump newConditional = new StatementConditionalJump(conditionAssignment.getrValue2(), destLabel, conditional.getSource());
newConditional.setDeclaredUnroll(conditional.isDeclaredUnroll());
newBlock.getStatements().add(newConditional);
newBlock.setDefaultSuccessor(block.getDefaultSuccessor());
newBlock.setConditionalSuccessor(destLabel);
// Rewrite the conditional to use only the first part of the && condition expression
@ -119,13 +121,17 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization {
Label newBlockLabel = currentScope.addLabelIntermediate();
ControlFlowBlock newBlock = new ControlFlowBlock(newBlockLabel.getRef(), currentScopeRef);
getGraph().addBlock(newBlock);
newBlock.getStatements().add(new StatementConditionalJump(conditionAssignment.getrValue2(), conditional.getDestination(), conditional.getSource()));
StatementConditionalJump newConditional = new StatementConditionalJump(conditionAssignment.getrValue2(), conditional.getDestination(), conditional.getSource());
// Copy unrolling to the new conditional
newConditional.setDeclaredUnroll(conditional.isDeclaredUnroll());
newBlock.getStatements().add(newConditional);
newBlock.setConditionalSuccessor(conditional.getDestination());
newBlock.setDefaultSuccessor(block.getDefaultSuccessor());
// Rewrite the conditional to use only the first part of the && condition expression
block.setDefaultSuccessor(newBlockLabel.getRef());
conditional.setrValue2(conditionAssignment.getrValue1());
// Remove any unrolling from the original conditional as only the new one leaves the loop
conditional.setDeclaredUnroll(false);
// TODO: Fix phi-values inside the destination phi-blocks to reflect the new control flow! Use replaceLabels(block, replacement)
ControlFlowBlock conditionalDestBlock = getGraph().getBlock(conditional.getDestination());

View File

@ -7,9 +7,9 @@ import java.util.ArrayList;
import java.util.List;
/** Finds the dominators for the control flow graph. */
public class Pass3DominatorsAnalysis extends Pass2Base {
public class Pass2DominatorsAnalysis extends Pass2SsaOptimization {
public Pass3DominatorsAnalysis(Program program) {
public Pass2DominatorsAnalysis(Program program) {
super(program);
}
@ -19,10 +19,9 @@ public class Pass3DominatorsAnalysis extends Pass2Base {
* Definition: d dom i if all paths from entry to node i include d
* <p>
* See http://www.cs.colostate.edu/~cs553/ClassNotes/lecture09-control-dominators.ppt.pdf
*
* @return The graph dominators
*/
public void findDominators() {
@Override
public boolean step() {
DominatorsGraph dominatorsGraph = new DominatorsGraph();
// Initialize dominators: Dom[first]={first}, Dom[block]={all}
@ -75,6 +74,7 @@ public class Pass3DominatorsAnalysis extends Pass2Base {
} while(change);
getProgram().setDominators(dominatorsGraph);
return false;
}

View File

@ -11,19 +11,21 @@ import java.util.*;
* <p>
* See http://www.cs.colostate.edu/~cs553/ClassNotes/lecture09-control-dominators.ppt.pdf
*/
public class Pass3LoopAnalysis extends Pass2Base {
public class Pass2LoopAnalysis extends Pass2SsaOptimization {
public Pass3LoopAnalysis(Program program) {
public Pass2LoopAnalysis(Program program) {
super(program);
}
/**
* Finds loops and nested loops in the control flow graph.
* Uses the dominators of the graph to find loops.
* <p>
* See http://www.cs.colostate.edu/~cs553/ClassNotes/lecture09-control-dominators.ppt.pdf
*/
public void findLoops() {
@Override
public boolean step() {
DominatorsGraph dominators = getProgram().getDominators();
Collection<ControlFlowBlock> blocks = getGraph().getAllBlocks();
@ -35,7 +37,9 @@ public class Pass3LoopAnalysis extends Pass2Base {
if(blockDominators.contains(successor)) {
// Found a loop back edge!
NaturalLoop loop = new NaturalLoop(successor, block.getLabel());
getLog().append("Found back edge: " + loop.toString());
if(getLog().isVerboseSSAOptimize()) {
getLog().append("Found back edge: " + loop.toString());
}
loopSet.addLoop(loop);
}
}
@ -61,15 +65,17 @@ public class Pass3LoopAnalysis extends Pass2Base {
}
}
loop.setBlocks(loopBlocks);
getLog().append("Populated: " + loop.toString());
if(getLog().isVerboseSSAOptimize()) {
getLog().append("Populated: " + loop.toString());
}
}
boolean coalesceMore = true;
while(coalesceMore) {
coalesceMore = coalesceLoops(loopSet);
}
getProgram().setLoopSet(loopSet);
return false;
}
/**

View File

@ -0,0 +1,230 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.StatementInfos;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.VariableVersion;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.*;
/** Unroll Loops declared as inline. */
public class Pass2LoopUnroll extends Pass2SsaOptimization {
public Pass2LoopUnroll(Program program) {
super(program);
}
@Override
public boolean step() {
// Look for loops to unroll
NaturalLoopSet loops = getProgram().getLoopSet();
List<NaturalLoop> unrollLoopCandidates = findUnrollLoopCandidates(loops);
// Is there any unrolling to do?
if(unrollLoopCandidates.isEmpty()) {
return false;
}
// Choose the candidate loop with the fewest blocks (and if several have the same number of blocks the first one encountered)
NaturalLoop unrollLoop = chooseUnrollLoop(unrollLoopCandidates);
getLog().append("Unrolling loop " + unrollLoop);
List<ControlFlowBlock> unrollBlocks = new ArrayList<>();
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
if(unrollLoop.getBlocks().contains(block.getLabel())) {
unrollBlocks.add(block);
}
}
// Unroll the first iteration of the loop
// 0. Unroll Symbols
// - Create new versions of all symbols assigned inside the loop
Map<VariableRef, VariableRef> definedToNewVar = new LinkedHashMap<>();
VariableReferenceInfos variableReferenceInfos = getProgram().getVariableReferenceInfos();
for(ControlFlowBlock block : unrollBlocks) {
for(Statement statement : block.getStatements()) {
Collection<VariableRef> definedVars = variableReferenceInfos.getDefinedVars(statement);
for(VariableRef definedVarRef : definedVars) {
Variable definedVar = getScope().getVariable(definedVarRef);
Variable newVar;
if(definedVarRef.isIntermediate()) {
newVar = definedVar.getScope().addVariableIntermediate();
} else if(definedVarRef.isVersion()) {
newVar = ((VariableVersion) definedVar).getVersionOf().createVersion();
} else {
throw new RuntimeException("Error! Variable is not versioned or intermediate " + definedVar.toString(getProgram()));
}
definedToNewVar.put(definedVarRef, newVar.getRef());
getLog().append("Defined in loop: " + definedVarRef.getFullName() + " -> " + newVar.getRef().getFullName());
}
}
}
// - Ensure that all variables assigned inside the loop has a PHI in successor blocks to the loop
// - Find loop successor blocks
List<LabelRef> loopSuccessors = new ArrayList<>();
List<LabelRef> loopSuccessorPredecessors = new ArrayList<>();
for(ControlFlowBlock block : unrollBlocks) {
if(block.getDefaultSuccessor() != null && !unrollLoop.getBlocks().contains(block.getDefaultSuccessor())) {
// Default successor is outside
loopSuccessors.add(block.getDefaultSuccessor());
loopSuccessorPredecessors.add(block.getLabel());
}
if(block.getConditionalSuccessor() != null && !unrollLoop.getBlocks().contains(block.getConditionalSuccessor())) {
// Default successor is outside
loopSuccessors.add(block.getConditionalSuccessor());
loopSuccessorPredecessors.add(block.getLabel());
}
}
// - Add any needed PHI-statements to the successors
StatementInfos statementInfos = getProgram().getStatementInfos();
for(VariableRef definedVarRef : definedToNewVar.keySet()) {
// Find out if the variable is ever referenced outside the loop
boolean referencedOutsideLoop = false;
Collection<Integer> varRefStatements = variableReferenceInfos.getVarRefStatements(definedVarRef);
for(Integer varRefStatement : varRefStatements) {
ControlFlowBlock refBlock = statementInfos.getBlock(varRefStatement);
if(!unrollLoop.getBlocks().contains(refBlock.getLabel())) {
referencedOutsideLoop = true;
break;
}
}
if(referencedOutsideLoop) {
for(int i = 0; i < loopSuccessors.size(); i++) {
LabelRef successorBlockRef = loopSuccessors.get(i);
LabelRef successorPredecessorRef = loopSuccessorPredecessors.get(i);
ControlFlowBlock successorBlock = getGraph().getBlock(successorBlockRef);
StatementPhiBlock phiBlock = successorBlock.getPhiBlock();
// Look for a phi-variable
boolean phiFound = false;
for(StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) {
if(phiVariable.getVariable().isVersion() && definedVarRef.isVersion()) {
if(phiVariable.getVariable().getFullNameUnversioned().equals(definedVarRef.getFullNameUnversioned())) {
phiFound = true;
}
}
}
if(!phiFound) {
Variable definedVar = getScope().getVariable(definedVarRef);
Variable newVar = ((VariableVersion) definedVar).getVersionOf().createVersion();
StatementPhiBlock.PhiVariable newPhiVar = phiBlock.addPhiVariable(newVar.getRef());
newPhiVar.setrValue(successorPredecessorRef, definedVarRef);
getLog().append("Missing PHI for " + definedVarRef.getFullName() + " in block " + successorBlock.getLabel() + " - " + phiBlock.toString(getProgram(), false));
// TODO: Replace all references to definedVarRef outside loop to newVar!
}
}
}
}
getLog().append(getGraph().toString(getProgram()));
// 1. Copy all loop blocks to create the "rest of the loop" and modifying the existing loop to only be the first iteration)
// - Unroll Statements (copy all statements, replace symbols properly (with the new versions / the versions assigned in the existing loop)
// - Includes unrolling PHI-statements properly
// - Unroll Successors (loop-exit successors should point to the same exit, loop-internal successors should point to the new loop-internal block)
for(ControlFlowBlock block : unrollBlocks) {
// Find the serial number
int unrollSerial = 1;
String unrollLabelName = block.getLabel() + "_" + unrollSerial;
while(getScope().getLabel(unrollLabelName) != null) {
unrollSerial++;
}
// Create a label
Label unrollLabel = getScope().getScope(block.getScope()).addLabel(unrollLabelName);
// Create the new block
ControlFlowBlock unrollBlock = new ControlFlowBlock(unrollLabel.getRef(), block.getScope());
getProgram().getGraph().addBlock(unrollBlock);
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementPhiBlock) {
StatementPhiBlock phiBlock = (StatementPhiBlock) statement;
StatementPhiBlock unrollPhiBlock = new StatementPhiBlock();
for(StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) {
VariableRef phiVar = phiVariable.getVariable();
VariableRef newVar = definedToNewVar.get(phiVar);
StatementPhiBlock.PhiVariable unrollPhiVariable = unrollPhiBlock.addPhiVariable(newVar);
for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
/* FIX */
LabelRef predecessor = phiRValue.getPredecessor();
/* FIX */
RValue rValue = phiRValue.getrValue();
unrollPhiVariable.setrValue(predecessor, rValue);
}
}
} else {
throw new RuntimeException("Statement not handled by unroll " + statement);
}
}
}
// Patch the "old loop" to only contain the first iteration
// - All successors in the old loop to the loop head should point to the new loop head instead.
// - Remove phi-variables in the old head from looping. (as this predecessor now no longer exists)
// - Remove the "unroll" directive on the condition in the old loop (as it is already unrolled).
return false;
}
/**
* Choose which loop to unroll first from a candidate set.
* The smallest loop (fewest control flow blocks) is chosen.
* If multiple loops have the same size the first one is chosen.
*
* @param unrollLoopCandidates All loops that are decalred to be unrolled
* @return The loop to unroll first.
*/
private NaturalLoop chooseUnrollLoop(List<NaturalLoop> unrollLoopCandidates) {
NaturalLoop unrollLoop = null;
for(NaturalLoop unrollLoopCandidate : unrollLoopCandidates) {
if(unrollLoop == null) {
unrollLoop = unrollLoopCandidate;
} else {
if(unrollLoopCandidate.getBlocks().size() < unrollLoop.getBlocks().size()) {
unrollLoop = unrollLoopCandidate;
}
}
}
return unrollLoop;
}
/**
* Find all loops declared for unrolling. This is done by examining all conditional jumps, which hold the loop unroll declaration.
*
* @param loops All loops identified in the program
* @return All loops declared to be unrolled
*/
private List<NaturalLoop> findUnrollLoopCandidates(NaturalLoopSet loops) {
List<NaturalLoop> unrollLoopCandidates = new ArrayList<>();
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementConditionalJump) {
if(((StatementConditionalJump) statement).isDeclaredUnroll()) {
// Found a candidate for unrolling - identify the loop
for(NaturalLoop loopCandidate : loops.getLoopsContainingBlock(block.getLabel())) {
if(!loopCandidate.getBlocks().contains(block.getConditionalSuccessor())) {
// The conditional jump exits the loopCandidate - so we have identified the right one!
unrollLoopCandidates.add(loopCandidate);
break;
} else if(!loopCandidate.getBlocks().contains(block.getDefaultSuccessor())) {
// The default successor of the conditional exits the loopCandidate - so we have identified the right one!
unrollLoopCandidates.add(loopCandidate);
break;
}
}
}
}
}
}
return unrollLoopCandidates;
}
}

View File

@ -1,7 +1,8 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementInfos;
import dk.camelot64.kickc.model.values.LabelRef;
@ -11,16 +12,18 @@ import java.util.LinkedHashMap;
/**
* Identify the block for each statement.
*/
public class Pass3StatementInfos extends Pass2Base {
public class Pass3StatementInfos extends Pass2SsaOptimization {
public Pass3StatementInfos(Program program) {
super(program);
}
/**
* Create map from statement index to block
*/
public void generateStatementInfos() {
@Override
public boolean step() {
LinkedHashMap<Integer, LabelRef> stmtBlocks = new LinkedHashMap<>();
LinkedHashMap<Integer, Statement> stmtIdx = new LinkedHashMap<>();
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
@ -30,6 +33,7 @@ public class Pass3StatementInfos extends Pass2Base {
}
}
getProgram().setStatementInfos(new StatementInfos(getProgram(), stmtBlocks, stmtIdx));
return false;
}

View File

@ -24,8 +24,8 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
@Override
public boolean step() {
new PassNStatementIndices(getProgram()).generateStatementIndices();
new PassNVariableReferenceInfos(getProgram()).generateVariableReferenceInfos();
new PassNStatementIndices(getProgram()).execute();
new PassNVariableReferenceInfos(getProgram()).execute();
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
boolean modified = false;
for(ControlFlowBlock block : getGraph().getAllBlocks()) {

View File

@ -8,22 +8,25 @@ import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
public class PassNStatementIndices extends Pass2Base {
public class PassNStatementIndices extends Pass2SsaOptimization {
public PassNStatementIndices(Program program) {
super(program);
}
/**
* Create index numbers for all statements in the control flow graph.
*/
public void generateStatementIndices() {
@Override
public boolean step() {
int currentIdx = 0;
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
statement.setIndex(currentIdx++);
}
}
return false;
}
/**

View File

@ -17,62 +17,15 @@ import java.util.*;
/**
* Identify variables defined/referenced for each block & statement.
*/
public class PassNVariableReferenceInfos extends Pass2Base {
public class PassNVariableReferenceInfos extends Pass2SsaOptimization {
public PassNVariableReferenceInfos(Program program) {
super(program);
}
/**
* Get all variables referenced in an rValue
*
* @param rValue The rValue
* @return All referenced variables
*/
public static Collection<VariableRef> getReferencedVars(RValue rValue) {
LinkedHashSet<VariableRef> referencedVars = new LinkedHashSet<>();
for(SymbolRef symbolRef : getReferenced(rValue)) {
if(symbolRef instanceof VariableRef) {
referencedVars.add((VariableRef) symbolRef);
}
}
return referencedVars;
}
/**
* Get all constants referenced in an rValue
*
* @param rValue The rValue
* @return All referenced constants
*/
public static Collection<ConstantRef> getReferencedConsts(RValue rValue) {
LinkedHashSet<ConstantRef> referencedConsts = new LinkedHashSet<>();
for(SymbolRef symbolRef : getReferenced(rValue)) {
if(symbolRef instanceof ConstantRef) {
referencedConsts.add((ConstantRef) symbolRef);
}
}
return referencedConsts;
}
/**
* Get all variables /constants referenced in an rValue
*
* @param rValue The rValue
* @return All referenced variables / constants
*/
private static Collection<SymbolVariableRef> getReferenced(RValue rValue) {
Collection<SymbolVariableRef> referenced = new LinkedHashSet<>();
ProgramValueIterator.execute(new ProgramValue.GenericValue(rValue), (programValue, currentStmt, stmtIt, currentBlock) -> {
if(programValue.get() instanceof SymbolVariableRef) {
referenced.add((SymbolVariableRef) programValue.get());
}
}, null, null, null);
return referenced;
}
/** Create defined/referenced maps */
public void generateVariableReferenceInfos() {
@Override
public boolean step() {
LinkedHashMap<LabelRef, Collection<VariableRef>> blockReferencedVars = new LinkedHashMap<>();
LinkedHashMap<LabelRef, Collection<VariableRef>> blockUsedVars = new LinkedHashMap<>();
LinkedHashMap<Integer, Collection<VariableRef>> stmtReferenced = new LinkedHashMap<>();
@ -128,6 +81,56 @@ public class PassNVariableReferenceInfos extends Pass2Base {
});
}
getProgram().setVariableReferenceInfos(new VariableReferenceInfos(blockReferencedVars, blockUsedVars, stmtReferenced, stmtDefined, symbolVarReferences));
return false;
}
/**
* Get all variables referenced in an rValue
*
* @param rValue The rValue
* @return All referenced variables
*/
public static Collection<VariableRef> getReferencedVars(RValue rValue) {
LinkedHashSet<VariableRef> referencedVars = new LinkedHashSet<>();
for(SymbolRef symbolRef : getReferenced(rValue)) {
if(symbolRef instanceof VariableRef) {
referencedVars.add((VariableRef) symbolRef);
}
}
return referencedVars;
}
/**
* Get all constants referenced in an rValue
*
* @param rValue The rValue
* @return All referenced constants
*/
public static Collection<ConstantRef> getReferencedConsts(RValue rValue) {
LinkedHashSet<ConstantRef> referencedConsts = new LinkedHashSet<>();
for(SymbolRef symbolRef : getReferenced(rValue)) {
if(symbolRef instanceof ConstantRef) {
referencedConsts.add((ConstantRef) symbolRef);
}
}
return referencedConsts;
}
/**
* Get all variables /constants referenced in an rValue
*
* @param rValue The rValue
* @return All referenced variables / constants
*/
private static Collection<SymbolVariableRef> getReferenced(RValue rValue) {
Collection<SymbolVariableRef> referenced = new LinkedHashSet<>();
ProgramValueIterator.execute(new ProgramValue.GenericValue(rValue), (programValue, currentStmt, stmtIt, currentBlock) -> {
if(programValue.get() instanceof SymbolVariableRef) {
referenced.add((SymbolVariableRef) programValue.get());
}
}, null, null, null);
return referenced;
}
/**

View File

@ -46,10 +46,20 @@ public class TestPrograms {
AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false);
}
// @Test
// public void testUnrollScreenFill() throws IOException, URISyntaxException {
// compileAndCompare("unroll-screenfill");
// }
@Test
public void testUnusedBlockProblem() throws IOException, URISyntaxException {
compileAndCompare("unusedblockproblem");
}
@Test
public void testUnrollScreenFill() throws IOException, URISyntaxException {
compileAndCompare("unroll-screenfill");
}
@Test
public void testLoop100() throws IOException, URISyntaxException {
compileAndCompare("loop100");
}
@Test
public void testIrqHardwareClobberJsr() throws IOException, URISyntaxException {

View File

@ -0,0 +1,4 @@
void main() {
for(byte i=0; i<100; i++) { }
}

View File

@ -2,9 +2,34 @@
void main() {
byte* SCREEN = $400;
byte a=3;
inline do {
SCREEN[a]=a;
a++;
} while(a<14);
SCREEN[a]=a;
/*
for(byte x: 0..39) {
inline for(byte line: 0..24) {
(SCREEN+line*40)[x] = x;
}
}
*/
/*
for(byte x: 0..39) {
byte line = 0;
inline while(line!=25 || x<10) {
(SCREEN+line*40)[x] = x;
line++;
}
}
*/
}

View File

@ -0,0 +1,11 @@
// Problem with eliminating unused blocks/vars after the infinite loop (symbol line#2 not removed from symbol table)
void main() {
byte* SCREEN = $400;
while(true) {
(*SCREEN)++;
}
for(byte line: 0..24) {
SCREEN[line] = line;
}
}