1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-24 14:31:15 +00:00

Added constant loop-head detection to a switchable optimization option -Oloophead. #246

Implemented for()-loop condition checking before body. Closes #183
This commit is contained in:
jespergravgaard 2019-08-07 11:09:29 +02:00
commit c88932d423
24 changed files with 793 additions and 156 deletions

View File

@ -31,6 +31,9 @@ public class Compiler {
/** Enable the zero-page coalesce pass. It takes a lot of time, but limits the zero page usage significantly. */ /** Enable the zero-page coalesce pass. It takes a lot of time, but limits the zero page usage significantly. */
private boolean enableZeroPageCoalasce = false; private boolean enableZeroPageCoalasce = false;
/** Enable loop head constant optimization. It identified whenever a while()/for() has a constant condition on the first iteration and rewrites it. */
private boolean enableLoopHeadConstant = false;
public Compiler() { public Compiler() {
this.program = new Program(); this.program = new Program();
} }
@ -43,6 +46,10 @@ public class Compiler {
this.enableZeroPageCoalasce = true; this.enableZeroPageCoalasce = true;
} }
void enableLoopHeadConstant() {
this.enableLoopHeadConstant = true;
}
void setTargetPlatform(TargetPlatform targetPlatform) { void setTargetPlatform(TargetPlatform targetPlatform) {
program.setTargetPlatform(targetPlatform); program.setTargetPlatform(targetPlatform);
} }
@ -305,10 +312,13 @@ public class Compiler {
optimizations.add(new PassNSimplifyExpressionWithZero(program)); optimizations.add(new PassNSimplifyExpressionWithZero(program));
optimizations.add(new PassNEliminateUnusedVars(program, true)); optimizations.add(new PassNEliminateUnusedVars(program, true));
optimizations.add(new Pass2EliminateUnusedBlocks(program)); optimizations.add(new Pass2EliminateUnusedBlocks(program));
if(enableLoopHeadConstant) {
optimizations.add(new PassNStatementIndices(program)); optimizations.add(new PassNStatementIndices(program));
optimizations.add(() -> { program.clearDominators(); return false; }); optimizations.add(() -> { program.clearDominators(); return false; });
optimizations.add(() -> { program.clearLoopSet(); return false; }); optimizations.add(() -> { program.clearLoopSet(); return false; });
optimizations.add(new Pass2LoopHeadConstantIdentification(program)); optimizations.add(new Pass2LoopHeadConstantIdentification(program));
optimizations.add(() -> { program.clearStatementIndices(); return false; });
}
return optimizations; return optimizations;
} }

View File

@ -66,6 +66,9 @@ public class KickC implements Callable<Void> {
@CommandLine.Option(names = {"-Ocoalesce"}, description = "Optimization Option. Enables zero-page coalesce pass which limits zero-page usage significantly, but takes a lot of compile time.") @CommandLine.Option(names = {"-Ocoalesce"}, description = "Optimization Option. Enables zero-page coalesce pass which limits zero-page usage significantly, but takes a lot of compile time.")
private boolean optimizeZeroPageCoalesce = false; private boolean optimizeZeroPageCoalesce = false;
@CommandLine.Option(names = {"-Oloophead"}, description = "Optimization Option. Enables loop-head constant pass which identifies loops where the condition is constant on the first iteration.")
private boolean optimizeLoopHeadConstant = false;
@CommandLine.Option(names = {"-Ocache"}, description = "Optimization Option. Enables a fragment cache file.") @CommandLine.Option(names = {"-Ocache"}, description = "Optimization Option. Enables a fragment cache file.")
private boolean optimizeFragmentCache = false; private boolean optimizeFragmentCache = false;
@ -212,6 +215,10 @@ public class KickC implements Callable<Void> {
compiler.enableZeroPageCoalasce(); compiler.enableZeroPageCoalasce();
} }
if(optimizeLoopHeadConstant) {
compiler.enableLoopHeadConstant();
}
System.out.println("Compiling " + kcFile); System.out.println("Compiling " + kcFile);
Program program = null; Program program = null;
try { try {

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementInfos; import dk.camelot64.kickc.model.statements.StatementInfos;
import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.LabelRef;
@ -302,6 +303,17 @@ public class Program {
this.statementInfos = null; this.statementInfos = null;
} }
/**
* Clear index numbers for all statements in the control flow graph.
*/
public void clearStatementIndices() {
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
statement.setIndex(null);
}
}
}
public SymbolInfos getSymbolInfos() { public SymbolInfos getSymbolInfos() {
if(symbolInfos==null) if(symbolInfos==null)
this.symbolInfos = new PassNCalcSymbolInfos(this).calculate(); this.symbolInfos = new PassNCalcSymbolInfos(this).calculate();

View File

@ -8,11 +8,10 @@ import dk.camelot64.kickc.model.values.*;
/** /**
* Any Value in the program being iterated by {@link ProgramValueIterator}. * Any Value in the program being iterated by {@link ProgramValueIterator}.
* * <p>
* The Value can be inspected using get() and replaced inside the model using set(val). * The Value can be inspected using get() and replaced inside the model using set(val).
* * <p>
* The context of the Value can be determined from the sub-class containing it plus the parameters to the ProgramValueHandler. * The context of the Value can be determined from the sub-class containing it plus the parameters to the ProgramValueHandler.
*
*/ */
public interface ProgramValue { public interface ProgramValue {
@ -204,6 +203,11 @@ public interface ProgramValue {
public void set(Value value) { public void set(Value value) {
phiVariable.getValues().get(i).setrValue((RValue) value); phiVariable.getValues().get(i).setrValue((RValue) value);
} }
public LabelRef getPredecessor() {
return phiVariable.getValues().get(i).getPredecessor();
}
} }
class PhiValuePredecessor implements ProgramValue { class PhiValuePredecessor implements ProgramValue {

View File

@ -3,7 +3,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.ConstantBool;
import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantEnumerable;
import dk.camelot64.kickc.model.values.ConstantLiteral; import dk.camelot64.kickc.model.values.ConstantLiteral;
import java.util.Objects; import java.util.Objects;
@ -17,8 +17,8 @@ public class OperatorEqual extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(Objects.equals(((ConstantInteger) left).getInteger(), ((ConstantInteger) right).getInteger())); return new ConstantBool(Objects.equals(((ConstantEnumerable) left).getInteger(), ((ConstantEnumerable) right).getInteger()));
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -2,9 +2,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
/** Binary greater-than Operator ( x > y ) */ /** Binary greater-than Operator ( x > y ) */
public class OperatorGreaterThan extends OperatorBinary { public class OperatorGreaterThan extends OperatorBinary {
@ -15,8 +13,8 @@ public class OperatorGreaterThan extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(((ConstantInteger) left).getInteger() > ((ConstantInteger) right).getInteger()); return new ConstantBool(((ConstantEnumerable) left).getInteger() > ((ConstantEnumerable) right).getInteger());
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -2,9 +2,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
/** Binary greater-than-equal Operator ( x >= y ) */ /** Binary greater-than-equal Operator ( x >= y ) */
public class OperatorGreaterThanEqual extends OperatorBinary { public class OperatorGreaterThanEqual extends OperatorBinary {
@ -15,8 +13,8 @@ public class OperatorGreaterThanEqual extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(((ConstantInteger) left).getInteger() >= ((ConstantInteger) right).getInteger()); return new ConstantBool(((ConstantEnumerable) left).getInteger() >= ((ConstantEnumerable) right).getInteger());
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -3,7 +3,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.ConstantBool;
import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantEnumerable;
import dk.camelot64.kickc.model.values.ConstantLiteral; import dk.camelot64.kickc.model.values.ConstantLiteral;
/** Binary less-than Operator ( x < y ) */ /** Binary less-than Operator ( x < y ) */
@ -15,8 +15,8 @@ public class OperatorLessThan extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(((ConstantInteger) left).getInteger() < ((ConstantInteger) right).getInteger()); return new ConstantBool(((ConstantEnumerable) left).getInteger() < ((ConstantEnumerable) right).getInteger());
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -2,9 +2,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
/** Binary less-than-equal Operator ( x <= y ) */ /** Binary less-than-equal Operator ( x <= y ) */
public class OperatorLessThanEqual extends OperatorBinary { public class OperatorLessThanEqual extends OperatorBinary {
@ -15,8 +13,8 @@ public class OperatorLessThanEqual extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(((ConstantInteger) left).getInteger() <= ((ConstantInteger) right).getInteger()); return new ConstantBool(((ConstantEnumerable) left).getInteger() <= ((ConstantEnumerable) right).getInteger());
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -2,10 +2,7 @@ package dk.camelot64.kickc.model.operators;
import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantBool; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
import dk.camelot64.kickc.model.values.ConstantPointer;
import java.util.Objects; import java.util.Objects;
@ -18,10 +15,8 @@ public class OperatorNotEqual extends OperatorBinary {
@Override @Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) { public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
if(left instanceof ConstantInteger && right instanceof ConstantInteger) { if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
return new ConstantBool(!Objects.equals(((ConstantInteger) left).getInteger(), ((ConstantInteger) right).getInteger())); return new ConstantBool(!Objects.equals(((ConstantEnumerable) left).getInteger(), ((ConstantEnumerable) right).getInteger()));
} else if(left instanceof ConstantPointer && right instanceof ConstantPointer) {
return new ConstantBool(!Objects.equals(((ConstantPointer) left).getLocation(), ((ConstantPointer) right).getLocation()));
} }
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
} }

View File

@ -945,16 +945,32 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
BlockScope blockScope = getCurrentScope().addBlockScope(); BlockScope blockScope = getCurrentScope().addBlockScope();
scopeStack.push(blockScope); scopeStack.push(blockScope);
loopStack.push(new Loop(blockScope)); loopStack.push(new Loop(blockScope));
// Add initialization
this.visit(ctx.forClassicInit()); this.visit(ctx.forClassicInit());
KickCParser.StmtForContext stmtForCtx = (KickCParser.StmtForContext) ctx.getParent(); KickCParser.StmtForContext stmtForCtx = (KickCParser.StmtForContext) ctx.getParent();
// Add label // Add label
Label repeatLabel = getCurrentScope().addLabelIntermediate(); Label beginJumpLabel = getCurrentScope().addLabelIntermediate();
Label doJumpLabel = getCurrentScope().addLabelIntermediate();
Label endJumpLabel = getCurrentScope().addLabelIntermediate();
List<Comment> comments = getCommentsSymbol(stmtForCtx); List<Comment> comments = getCommentsSymbol(stmtForCtx);
StatementLabel repeatTarget = new StatementLabel(repeatLabel.getRef(), StatementSource.forClassic(ctx), comments); StatementLabel repeatTarget = new StatementLabel(beginJumpLabel.getRef(), StatementSource.forClassic(ctx), comments);
sequence.addStatement(repeatTarget); sequence.addStatement(repeatTarget);
// Add condition
KickCParser.CommaExprContext conditionCtx = ctx.commaExpr(0);
PrePostModifierHandler.addPreModifiers(this, conditionCtx, StatementSource.forClassic(ctx));
RValue rValue = (RValue) this.visit(conditionCtx);
PrePostModifierHandler.addPostModifiers(this, conditionCtx, StatementSource.forClassic(ctx));
// Add jump if condition was met
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, doJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
sequence.addStatement(doJmpStmt);
Statement endJmpStmt = new StatementJump(endJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
sequence.addStatement(endJmpStmt);
StatementLabel doJumpTarget = new StatementLabel(doJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
sequence.addStatement(doJumpTarget);
// Reuse the begin jump target for continue.
loopStack.peek().setContinueLabel(beginJumpLabel);
// Add body // Add body
addLoopBody(stmtForCtx.stmt()); addLoopBody(stmtForCtx.stmt());
addLoopContinueLabel(loopStack.peek(), ctx);
// Add increment // Add increment
KickCParser.CommaExprContext incrementCtx = ctx.commaExpr(1); KickCParser.CommaExprContext incrementCtx = ctx.commaExpr(1);
if(incrementCtx != null) { if(incrementCtx != null) {
@ -962,14 +978,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
this.visit(incrementCtx); this.visit(incrementCtx);
PrePostModifierHandler.addPostModifiers(this, incrementCtx, StatementSource.forClassic(ctx)); PrePostModifierHandler.addPostModifiers(this, incrementCtx, StatementSource.forClassic(ctx));
} }
// Add condition // Jump back to beginning
KickCParser.CommaExprContext conditionCtx = ctx.commaExpr(0); Statement beginJmpStmt = new StatementJump(beginJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
PrePostModifierHandler.addPreModifiers(this, conditionCtx, StatementSource.forClassic(ctx)); sequence.addStatement(beginJmpStmt);
RValue rValue = (RValue) this.visit(conditionCtx); StatementLabel endJumpTarget = new StatementLabel(endJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
PrePostModifierHandler.addPostModifiers(this, conditionCtx, StatementSource.forClassic(ctx)); sequence.addStatement(endJumpTarget);
// Add jump if condition was met
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, repeatLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
sequence.addStatement(doJmpStmt);
addDirectives(doJmpStmt, stmtForCtx.directive()); addDirectives(doJmpStmt, stmtForCtx.directive());
addLoopBreakLabel(loopStack.pop(), ctx); addLoopBreakLabel(loopStack.pop(), ctx);
scopeStack.pop(); scopeStack.pop();

View File

@ -39,7 +39,7 @@ public class Pass1AssertUsedVars extends Pass1Base {
ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME)); ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME));
assertUsedVars(beginBlock, null, referenceInfos, new LinkedHashSet<>(), new LinkedHashSet<>()); assertUsedVars(beginBlock, null, referenceInfos, new LinkedHashSet<>(), new LinkedHashSet<>());
getProgram().clearVariableReferenceInfos(); getProgram().clearVariableReferenceInfos();
new PassNStatementIndices(getProgram()).clearStatementIndices(); getProgram().clearStatementIndices();
return false; return false;
} }

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc.passes; package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ConstantNotLiteral;
import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.operators.Operator; import dk.camelot64.kickc.model.operators.Operator;
@ -41,6 +42,7 @@ public class Pass2ConstantIfs extends Pass2SsaOptimization {
ConstantValue constValue1 = Pass2ConstantIdentification.getConstant(conditional.getrValue1()); ConstantValue constValue1 = Pass2ConstantIdentification.getConstant(conditional.getrValue1());
Operator operator = conditional.getOperator(); Operator operator = conditional.getOperator();
ConstantValue constValue2 = Pass2ConstantIdentification.getConstant(conditional.getrValue2()); ConstantValue constValue2 = Pass2ConstantIdentification.getConstant(conditional.getrValue2());
try {
if(conditional.getrValue1() == null && operator == null && constValue2 != null) { if(conditional.getrValue1() == null && operator == null && constValue2 != null) {
// Constant condition // Constant condition
literal = constValue2.calculateLiteral(getScope()); literal = constValue2.calculateLiteral(getScope());
@ -53,6 +55,9 @@ public class Pass2ConstantIfs extends Pass2SsaOptimization {
ConstantValue constVal = Pass2ConstantIdentification.createBinary(constValue1, (OperatorBinary) operator, constValue2, getScope()); ConstantValue constVal = Pass2ConstantIdentification.createBinary(constValue1, (OperatorBinary) operator, constValue2, getScope());
literal = constVal.calculateLiteral(getScope()); literal = constVal.calculateLiteral(getScope());
} }
} catch (ConstantNotLiteral e) {
// not literal - keep as null
}
if(literal!=null && literal instanceof ConstantBool) { if(literal!=null && literal instanceof ConstantBool) {
// Condition is a constant boolean // Condition is a constant boolean
if(((ConstantBool) literal).getBool()) { if(((ConstantBool) literal).getBool()) {

View File

@ -6,10 +6,7 @@ import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementConditionalJump; import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.ConstantValue; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.PointerDereference;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.passes.utils.Unroller; import dk.camelot64.kickc.passes.utils.Unroller;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,10 +31,9 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
for(NaturalLoop loop : loopSet.getLoops()) { for(NaturalLoop loop : loopSet.getLoops()) {
LabelRef loopHeadRef = loop.getHead(); LabelRef loopHeadRef = loop.getHead();
ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHeadRef); ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHeadRef);
//TODO: Fix remaining errors! boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos);
//boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos);
boolean modified = false;
if(modified) { if(modified) {
getProgram().clearVariableReferenceInfos();
getProgram().clearStatementInfos(); getProgram().clearStatementInfos();
getProgram().clearLoopSet(); getProgram().clearLoopSet();
getProgram().clearDominators(); getProgram().clearDominators();
@ -45,7 +41,7 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
} }
} }
// TODO: Move to Program // TODO: Move to Program
new PassNStatementIndices(getProgram()).clearStatementIndices(); getProgram().clearStatementIndices();
return false; return false;
} }
@ -74,6 +70,16 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
// Predecessor it outside the loop // Predecessor it outside the loop
if(value.getrValue() instanceof ConstantValue) { if(value.getrValue() instanceof ConstantValue) {
// The value is constant in the predecessor!! // The value is constant in the predecessor!!
// Make sure it can be calculated as a literal
boolean isLiteral = true;
try {
ConstantValue constantValue = (ConstantValue) value.getrValue();
constantValue.calculateLiteral(getProgram().getScope());
} catch (ConstantNotLiteral e) {
// Not literal
isLiteral = false;
}
if(isLiteral) {
// Optimization of the loop head is a good idea for this variable! // Optimization of the loop head is a good idea for this variable!
optimizeVars.add(phiVariable.getVariable()); optimizeVars.add(phiVariable.getVariable());
} }
@ -81,6 +87,7 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
} }
} }
} }
}
// Is optimization a good idea? // Is optimization a good idea?
boolean doOptimize = true; boolean doOptimize = true;

View File

@ -140,7 +140,7 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
} }
getProgram().clearVariableReferenceInfos(); getProgram().clearVariableReferenceInfos();
new PassNStatementIndices(getProgram()).clearStatementIndices(); getProgram().clearStatementIndices();
return modified; return modified;
} }

View File

@ -29,16 +29,4 @@ public class PassNStatementIndices extends Pass2SsaOptimization {
return false; return false;
} }
/**
* Clear index numbers for all statements in the control flow graph.
*/
public void clearStatementIndices() {
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
statement.setIndex(null);
}
}
}
} }

View File

@ -183,32 +183,19 @@ public class PassNCalcVariableReferenceInfos extends PassNCalcBase<VariableRefer
* @param stmt The statement * @param stmt The statement
* @return Variables defined by the statement * @return Variables defined by the statement
*/ */
private Collection<VariableRef> getDefinedVars(Statement stmt) { public static Collection<VariableRef> getDefinedVars(Statement stmt) {
if(stmt instanceof StatementAssignment) { if(stmt instanceof StatementPhiBlock) {
StatementAssignment assignment = (StatementAssignment) stmt;
LValue lValue = assignment.getlValue();
if(lValue instanceof VariableRef) {
return Collections.singletonList((VariableRef) lValue);
}
} else if(stmt instanceof StatementPhiBlock) {
List<VariableRef> defined = new ArrayList<>(); List<VariableRef> defined = new ArrayList<>();
StatementPhiBlock phi = (StatementPhiBlock) stmt; StatementPhiBlock phi = (StatementPhiBlock) stmt;
for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) { for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {
defined.add(phiVariable.getVariable()); defined.add(phiVariable.getVariable());
} }
return defined; return defined;
} else if(stmt instanceof StatementCall) { } else if(stmt instanceof StatementLValue) {
List<VariableRef> defined = new ArrayList<>(); LValue lValue = ((StatementLValue) stmt).getlValue();
if(((StatementCall) stmt).getlValue() instanceof VariableRef) { if(lValue instanceof VariableRef) {
defined.add((VariableRef) ((StatementCall) stmt).getlValue()); return Collections.singletonList((VariableRef) lValue);
} }
return defined;
} else if(stmt instanceof StatementCallPointer) {
List<VariableRef> defined = new ArrayList<>();
if(((StatementCallPointer) stmt).getlValue() instanceof VariableRef) {
defined.add((VariableRef) ((StatementCallPointer) stmt).getlValue());
}
return defined;
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -10,8 +10,11 @@ import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.VariableVersion; import dk.camelot64.kickc.model.symbols.VariableVersion;
import dk.camelot64.kickc.model.values.*; import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.passes.AliasReplacer; import dk.camelot64.kickc.passes.AliasReplacer;
import dk.camelot64.kickc.passes.Pass1GenerateSingleStaticAssignmentForm;
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* Utility for copying blocks in a program - typically to unroll loops or conditions. * Utility for copying blocks in a program - typically to unroll loops or conditions.
@ -57,12 +60,10 @@ public class Unroller {
public void unroll() { public void unroll() {
// 0. Prepare for copying by ensuring that all variables defined in the blocks are represented in PHI-blocks of the successors // 0. Prepare for copying by ensuring that all variables defined in the blocks are represented in PHI-blocks of the successors
prepare(); prepare();
if(program.getLog().isVerboseLoopUnroll()) {
if(program.getLog().isVerboseSSAOptimize()) { program.getLog().append("CONTROL FLOW GRAPH (PREPARED FOR LOOP HEAD UNROLL)");
program.getLog().append("CONTROL FLOW GRAPH (PREPARED)");
program.getLog().append(program.getGraph().toString(program)); program.getLog().append(program.getGraph().toString(program));
} }
// 1. Create new versions of all symbols assigned inside the loop // 1. Create new versions of all symbols assigned inside the loop
this.varsOriginalToCopied = copyDefinedVars(unrollBlocks, program); this.varsOriginalToCopied = copyDefinedVars(unrollBlocks, program);
// 2. Create new labels for all blocks in the loop // 2. Create new labels for all blocks in the loop
@ -72,70 +73,144 @@ public class Unroller {
} }
/** /**
* Ensure that all variables defined inside the blocks to be copied has a PHI in successor blocks. * Ensure that variables defined inside and used outside the blocks to be copied has different versions in different successors blocks.
*/ */
private void prepare() { private void prepare() {
for(VariableRef origVarRef : getVarsDefinedIn(unrollBlocks, program)) { for(VariableRef origVarRef : getVarsDefinedIn(unrollBlocks, program)) {
// Find out if the variable is ever referenced outside the loop // Find out if the variable is ever referenced outside the loop
if(isReferencedOutside(origVarRef, unrollBlocks, program)) { if(isReferencedOutside(origVarRef, unrollBlocks, program)) {
// Add any needed PHI-statements to the successors // Re-version all usages of the specific variable version
for(SuccessorTransition successorTransition : getSuccessorTransitions(unrollBlocks, program.getGraph())) { Map<LabelRef, VariableRef> newPhis = new LinkedHashMap<>();
ControlFlowBlock successorBlock = program.getGraph().getBlock(successorTransition.successor); Map<LabelRef, VariableRef> varVersions = new LinkedHashMap<>();
StatementPhiBlock phiBlock = successorBlock.getPhiBlock(); reVersionAllUsages(origVarRef, newPhis, varVersions);
// Create a new version of the variable if(program.getLog().isVerboseLoopUnroll()) {
Variable origVar = program.getScope().getVariable(origVarRef); program.getLog().append("Created new versions for " + origVarRef + ")");
Variable newVar; //program.getLog().append(program.getGraph().toString(program));
if(origVar instanceof VariableVersion) {
newVar = ((VariableVersion) origVar).getVersionOf().createVersion();
} else {
newVar = origVar.getScope().addVariableIntermediate();
}
// Replace all references from the new phi and forward
forwardReplaceAllUsages(successorTransition.successor, origVarRef, newVar.getRef(), new LinkedHashSet<>());
// Create the new phi-variable in the successor phi block
StatementPhiBlock.PhiVariable newPhiVar = phiBlock.addPhiVariable(newVar.getRef());
newPhiVar.setrValue(successorTransition.predecessor, origVarRef);
program.getLog().append("Creating PHI for " + origVarRef.getFullName() + " in block " + successorBlock.getLabel() + " - " + phiBlock.toString(program, false));
} }
// Recursively fill out & add PHI-functions until they have propagated everywhere needed
completePhiFunctions(newPhis, varVersions);
} }
} }
} }
/** /**
* Introduces a new version of a variable - and replaces all uses of the old variable with the new one from a specific point in the control flow graph and forward until the old variable is defined. * Find all usages of a variable and create new versions for each usage.
* @param blockRef The block to replace the usage from *
* @param origVarRef The original variable * @param origVarRef The original variable where all usages must have new versions
* @param newVarRef The new variable replacing the original * @param newPhis Map that will be populated with all new (empty) PHI-variables for the new versionw - these will be populated later.
* @param visited All blocks that have already been visited. * @param varVersions Map that will be populated with the version of the origVariable at the end of each block where it has a defined version.
*/ */
private void forwardReplaceAllUsages(LabelRef blockRef, VariableRef origVarRef, VariableRef newVarRef, Set<LabelRef> visited) { private void reVersionAllUsages(VariableRef origVarRef, Map<LabelRef, VariableRef> newPhis, Map<LabelRef, VariableRef> varVersions) {
VariableReferenceInfos variableReferenceInfos = program.getVariableReferenceInfos();
LinkedHashMap<SymbolRef, RValue> aliases = new LinkedHashMap<>(); // First add the definition of origVar to varVersions
aliases.put(origVarRef, newVarRef); for(ControlFlowBlock block : program.getGraph().getAllBlocks()) {
AliasReplacer aliasReplacer = new AliasReplacer(aliases);
ControlFlowBlock block = program.getGraph().getBlock(blockRef);
if(block!=null) {
for(Statement statement : block.getStatements()) { for(Statement statement : block.getStatements()) {
Collection<VariableRef> definedVars = variableReferenceInfos.getDefinedVars(statement); Collection<VariableRef> definedVars = PassNCalcVariableReferenceInfos.getDefinedVars(statement);
if(definedVars!=null && definedVars.contains(origVarRef)) { if(definedVars.contains(origVarRef)) {
// Found definition of the original variable - don't replace any more varVersions.put(block.getLabel(), origVarRef);
return;
}
// Replace any usage in the statement
ProgramValueIterator.execute(statement, aliasReplacer, null, block);
} }
} }
visited.add(blockRef);
if(block!=null) {
if(block.getConditionalSuccessor() != null && !visited.contains(block.getConditionalSuccessor())) {
forwardReplaceAllUsages(block.getConditionalSuccessor(), origVarRef, newVarRef, visited);
} }
if(block.getDefaultSuccessor() != null && !visited.contains(block.getDefaultSuccessor())) { // Next iterate the entire graph ensuring that all usages create new versions (except usages right after the definition)
forwardReplaceAllUsages(block.getDefaultSuccessor(), origVarRef, newVarRef, visited); for(ControlFlowBlock block : program.getGraph().getAllBlocks()) {
AtomicReference<VariableRef> currentVersion = new AtomicReference<>();
// Set current version from map
currentVersion.set(varVersions.get(block.getLabel()));
for(Statement statement : block.getStatements()) {
ProgramValueIterator.execute(statement, (programValue, currentStmt, stmtIt, currentBlock) -> {
Value value = programValue.get();
if(origVarRef.equals(value)) {
// Found a reference!
if(statement instanceof StatementPhiBlock && programValue instanceof ProgramValue.PhiVariable) {
// This is the definition - don't replace it
currentVersion.set(origVarRef);
varVersions.put(block.getLabel(), origVarRef);
} else if(statement instanceof StatementLValue && programValue instanceof ProgramValue.ProgramValueLValue) {
// This is the definition - don't replace it
currentVersion.set(origVarRef);
varVersions.put(block.getLabel(), origVarRef);
} else if(statement instanceof StatementPhiBlock && programValue instanceof ProgramValue.PhiValue) {
// The reference is inside a PHI-value - we need a version in the predecessor
LabelRef predecessor = ((ProgramValue.PhiValue) programValue).getPredecessor();
VariableRef predecessorVersion = varVersions.get(predecessor);
if(predecessorVersion == null) {
// Add a new PHI to the predecessor
predecessorVersion = createNewVersion(origVarRef);
varVersions.put(predecessor, predecessorVersion);
newPhis.put(predecessor, predecessorVersion);
}
// Use the definition
programValue.set(predecessorVersion);
} else if(currentVersion.get() == null) {
// Found a reference - no definition - create a new version
VariableRef newVarRef = createNewVersion(origVarRef);
currentVersion.set(newVarRef);
varVersions.put(block.getLabel(), newVarRef);
newPhis.put(block.getLabel(), currentVersion.get());
// Use the definition
programValue.set(newVarRef);
} else {
programValue.set(currentVersion.get());
}
}
}, null, null);
}
}
// Add the new empty PHI-blocks()
for(LabelRef blockRef : newPhis.keySet()) {
ControlFlowBlock block = program.getGraph().getBlock(blockRef);
VariableRef newVersion = newPhis.get(blockRef);
block.getPhiBlock().addPhiVariable(newVersion);
}
}
/**
* Create a new version of a variable
* @param origVarRef The original variable
* @return The new version
*/
private VariableRef createNewVersion(VariableRef origVarRef) {
Variable origVar = program.getScope().getVariable(origVarRef);
Scope scope = origVar.getScope();
VariableRef newVarRef;
if(origVarRef.isIntermediate()) {
newVarRef = scope.addVariableIntermediate().getRef();
} else {
newVarRef = ((VariableVersion) origVar).getVersionOf().createVersion().getRef();
}
return newVarRef;
}
/**
* Look through all new phi-functions and fill out their parameters.
* Both passed maps are modified
*
* @param newPhis New (empty) PHI-variables for the new versions that need to be populated
* @param varVersions Map with the version of the origVariable at the end of each block where it has a defined version.
*/
private void completePhiFunctions(Map<LabelRef, VariableRef> newPhis, Map<LabelRef, VariableRef> varVersions) {
Map<LabelRef, VariableRef> todo = newPhis;
while(todo.size() > 0) {
Map<LabelRef, VariableRef> doing = todo;
todo = new LinkedHashMap<>();
for(LabelRef blockRef : doing.keySet()) {
VariableRef doingVarRef = doing.get(blockRef);
ControlFlowBlock block = program.getGraph().getBlock(blockRef);
StatementPhiBlock.PhiVariable doingPhiVariable = block.getPhiBlock().getPhiVariable(doingVarRef);
List<ControlFlowBlock> predecessors = Pass1GenerateSingleStaticAssignmentForm.getPhiPredecessors(block, program);
for(ControlFlowBlock predecessor : predecessors) {
VariableRef predecessorVarRef = varVersions.get(predecessor.getLabel());
if(predecessorVarRef == null) {
// Variable has no version in the predecessor block - add a new PHI and populate later!
VariableRef newVarRef = createNewVersion(doingVarRef);
predecessor.getPhiBlock().addPhiVariable(newVarRef);
varVersions.put(predecessor.getLabel(), newVarRef);
todo.put(predecessor.getLabel(), newVarRef);
predecessorVarRef = newVarRef;
}
doingPhiVariable.setrValue(predecessor.getLabel(), predecessorVarRef);
} }
if(block.getCallSuccessor() != null && !visited.contains(block.getCallSuccessor())) {
forwardReplaceAllUsages(block.getCallSuccessor(), origVarRef, newVarRef, visited);
} }
} }
} }
@ -272,7 +347,7 @@ public class Unroller {
} }
/** /**
* Patch the PHI-block of an external successor block. Ensures that the PHI-block also receives data from the new coped block. * Patch the PHI-block of an external successor block. Ensures that the PHI-block also receives data from the new copied block.
* *
* @param successor The successor block's label * @param successor The successor block's label
* @param origBlock The label of the original block * @param origBlock The label of the original block

View File

@ -1952,8 +1952,8 @@ public class TestPrograms {
} }
@Test @Test
public void testBoolIfs() throws IOException, URISyntaxException { public void testBoolIfsMin() throws IOException, URISyntaxException {
compileAndCompare("bool-ifs"); compileAndCompare("bool-ifs-min");
} }
@Test @Test

View File

@ -0,0 +1,12 @@
// A test of boolean conditions using && || and !
void main() {
const char* screen = 0x400;
for( char i : 0..20) {
if( (i<10) && ((i&1)==0) ) {
screen[i] = '*';
}
}
}

View File

@ -0,0 +1,22 @@
// A test of boolean conditions using && || and !
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
main: {
.label screen = $400
ldx #0
b1:
txa
and #1
cpx #$a
bcs b2
cmp #0
bne b2
lda #'*'
sta screen,x
b2:
inx
cpx #$15
bne b1
rts
}

View File

@ -0,0 +1,30 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@2
[5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 )
[6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1
[7] if((byte) main::i#2>=(byte) $a) goto main::@2
to:main::@4
main::@4: scope:[main] from main::@1
[8] if((byte~) main::$1!=(byte) 0) goto main::@2
to:main::@3
main::@3: scope:[main] from main::@4
[9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*'
to:main::@2
main::@2: scope:[main] from main::@1 main::@3 main::@4
[10] (byte) main::i#1 ← ++ (byte) main::i#2
[11] if((byte) main::i#1!=(byte) $15) goto main::@1
to:main::@return
main::@return: scope:[main] from main::@2
[12] return
to:@return

View File

@ -0,0 +1,458 @@
Culled Empty Block (label) main::@4
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
to:@1
main: scope:[main] from @1
(byte*) main::screen#0 ← ((byte*)) (number) $400
(byte) main::i#0 ← (byte) 0
to:main::@1
main::@1: scope:[main] from main main::@2
(byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 )
(bool~) main::$0 ← (byte) main::i#2 < (number) $a
(number~) main::$1 ← (byte) main::i#2 & (number) 1
(bool~) main::$2 ← (number~) main::$1 == (number) 0
(bool~) main::$3 ← (bool~) main::$0 && (bool~) main::$2
(bool~) main::$4 ← ! (bool~) main::$3
if((bool~) main::$4) goto main::@2
to:main::@3
main::@2: scope:[main] from main::@1 main::@3
(byte) main::i#3 ← phi( main::@1/(byte) main::i#2 main::@3/(byte) main::i#4 )
(byte) main::i#1 ← (byte) main::i#3 + rangenext(0,$14)
(bool~) main::$5 ← (byte) main::i#1 != rangelast(0,$14)
if((bool~) main::$5) goto main::@1
to:main::@return
main::@3: scope:[main] from main::@1
(byte) main::i#4 ← phi( main::@1/(byte) main::i#2 )
*((byte*) main::screen#0 + (byte) main::i#4) ← (byte) '*'
to:main::@2
main::@return: scope:[main] from main::@2
return
to:@return
@1: scope:[] from @begin
call main
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
SYMBOL TABLE SSA
(label) @1
(label) @2
(label) @begin
(label) @end
(void()) main()
(bool~) main::$0
(number~) main::$1
(bool~) main::$2
(bool~) main::$3
(bool~) main::$4
(bool~) main::$5
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@return
(byte) main::i
(byte) main::i#0
(byte) main::i#1
(byte) main::i#2
(byte) main::i#3
(byte) main::i#4
(byte*) main::screen
(byte*) main::screen#0
Adding number conversion cast (unumber) $a in (bool~) main::$0 ← (byte) main::i#2 < (number) $a
Adding number conversion cast (unumber) 1 in (number~) main::$1 ← (byte) main::i#2 & (number) 1
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (byte) main::i#2 & (unumber)(number) 1
Adding number conversion cast (unumber) 0 in (bool~) main::$2 ← (unumber~) main::$1 == (number) 0
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) main::screen#0 ← (byte*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast $a
Simplifying constant integer cast 1
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) $a
Finalized unsigned number type (byte) 1
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$1 ← (byte) main::i#2 & (byte) 1
Alias (byte) main::i#2 = (byte) main::i#4
Successful SSA optimization Pass2AliasElimination
Alias (byte) main::i#2 = (byte) main::i#3
Successful SSA optimization Pass2AliasElimination
Simple Condition (bool~) main::$5 [12] if((byte) main::i#1!=rangelast(0,$14)) goto main::@1
Successful SSA optimization Pass2ConditionalJumpSimplification
Rewriting ! if()-condition to reversed if() [7] (bool~) main::$4 ← ! (bool~) main::$3
Successful SSA optimization Pass2ConditionalAndOrRewriting
Rewriting && if()-condition to two if()s [6] (bool~) main::$3 ← (bool~) main::$0 && (bool~) main::$2
Successful SSA optimization Pass2ConditionalAndOrRewriting
Constant (const byte*) main::screen#0 = (byte*) 1024
Constant (const byte) main::i#0 = 0
Successful SSA optimization Pass2ConstantIdentification
Resolved ranged next value [10] main::i#1 ← ++ main::i#2 to ++
Resolved ranged comparison value [12] if(main::i#1!=rangelast(0,$14)) goto main::@1 to (number) $15
Adding number conversion cast (unumber) $15 in if((byte) main::i#1!=(number) $15) goto main::@1
Successful SSA optimization PassNAddNumberTypeConversions
Simplifying constant integer cast $15
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) $15
Successful SSA optimization PassNFinalizeNumberTypeConversions
Simple Condition (bool~) main::$0 [4] if((byte) main::i#2<(byte) $a) goto main::@5
Simple Condition (bool~) main::$2 [10] if((byte~) main::$1==(byte) 0) goto main::@3
Successful SSA optimization Pass2ConditionalJumpSimplification
Negating conditional jump and destination [4] if((byte) main::i#2>=(byte) $a) goto main::@2
Negating conditional jump and destination [10] if((byte~) main::$1!=(byte) 0) goto main::@2
Successful SSA optimization Pass2ConditionalJumpSequenceImprovement
Inlining constant with var siblings (const byte) main::i#0
Constant inlined main::i#0 = (byte) 0
Successful SSA optimization Pass2ConstantInlining
Added new block during phi lifting main::@6(between main::@2 and main::@1)
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
CALL GRAPH
Calls in [] to main:2
Created 1 initial phi equivalence classes
Coalesced [14] main::i#5 ← main::i#1
Coalesced down to 1 phi equivalence classes
Culled Empty Block (label) @2
Culled Empty Block (label) main::@6
Renumbering block main::@5 to main::@4
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@2
[5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 )
[6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1
[7] if((byte) main::i#2>=(byte) $a) goto main::@2
to:main::@4
main::@4: scope:[main] from main::@1
[8] if((byte~) main::$1!=(byte) 0) goto main::@2
to:main::@3
main::@3: scope:[main] from main::@4
[9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*'
to:main::@2
main::@2: scope:[main] from main::@1 main::@3 main::@4
[10] (byte) main::i#1 ← ++ (byte) main::i#2
[11] if((byte) main::i#1!=(byte) $15) goto main::@1
to:main::@return
main::@return: scope:[main] from main::@2
[12] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
(byte~) main::$1 11.0
(byte) main::i
(byte) main::i#1 16.5
(byte) main::i#2 11.0
(byte*) main::screen
Initial phi equivalence classes
[ main::i#2 main::i#1 ]
Added variable main::$1 to zero page equivalence class [ main::$1 ]
Complete equivalence classes
[ main::i#2 main::i#1 ]
[ main::$1 ]
Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
Allocated zp ZP_BYTE:3 [ main::$1 ]
INITIAL ASM
Target platform is c64basic
// File Comments
// A test of boolean conditions using && || and !
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label screen = $400
.label _1 = 3
.label i = 2
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
lda #0
sta i
jmp b1
// [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
b1_from_b2:
// [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy
jmp b1
// main::@1
b1:
// [6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1 -- vbuz1=vbuz2_band_vbuc1
lda #1
and i
sta _1
// [7] if((byte) main::i#2>=(byte) $a) goto main::@2 -- vbuz1_ge_vbuc1_then_la1
lda i
cmp #$a
bcs b2
jmp b4
// main::@4
b4:
// [8] if((byte~) main::$1!=(byte) 0) goto main::@2 -- vbuz1_neq_0_then_la1
lda _1
cmp #0
bne b2
jmp b3
// main::@3
b3:
// [9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2
lda #'*'
ldy i
sta screen,y
jmp b2
// main::@2
b2:
// [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1
inc i
// [11] if((byte) main::i#1!=(byte) $15) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
lda #$15
cmp i
bne b1_from_b2
jmp breturn
// main::@return
breturn:
// [12] return
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*' [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
Statement [6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1 [ main::i#2 main::$1 ] ( main:2 [ main::i#2 main::$1 ] ) always clobbers reg byte a
Statement [9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*' [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:3 [ main::$1 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 27.5: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 11: zp ZP_BYTE:3 [ main::$1 ]
Uplift Scope []
Uplifting [main] best 483 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$1 ]
Uplifting [] best 483 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// A test of boolean conditions using && || and !
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label screen = $400
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
ldx #0
jmp b1
// [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
b1_from_b2:
// [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy
jmp b1
// main::@1
b1:
// [6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1
txa
and #1
// [7] if((byte) main::i#2>=(byte) $a) goto main::@2 -- vbuxx_ge_vbuc1_then_la1
cpx #$a
bcs b2
jmp b4
// main::@4
b4:
// [8] if((byte~) main::$1!=(byte) 0) goto main::@2 -- vbuaa_neq_0_then_la1
cmp #0
bne b2
jmp b3
// main::@3
b3:
// [9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*' -- pbuc1_derefidx_vbuxx=vbuc2
lda #'*'
sta screen,x
jmp b2
// main::@2
b2:
// [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
inx
// [11] if((byte) main::i#1!=(byte) $15) goto main::@1 -- vbuxx_neq_vbuc1_then_la1
cpx #$15
bne b1_from_b2
jmp breturn
// main::@return
breturn:
// [12] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp b1
Removing instruction jmp b4
Removing instruction jmp b3
Removing instruction jmp b2
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Replacing label b1_from_b2 with b1
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend_from_b1:
Removing instruction b1_from_b2:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction b1_from_main:
Removing instruction b4:
Removing instruction b3:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction jmp b1
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(void()) main()
(byte~) main::$1 reg byte a 11.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@return
(byte) main::i
(byte) main::i#1 reg byte x 16.5
(byte) main::i#2 reg byte x 11.0
(byte*) main::screen
(const byte*) main::screen#0 screen = (byte*) 1024
reg byte x [ main::i#2 main::i#1 ]
reg byte a [ main::$1 ]
FINAL ASSEMBLER
Score: 291
// File Comments
// A test of boolean conditions using && || and !
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
.label screen = $400
// [5] phi from main to main::@1 [phi:main->main::@1]
// [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
ldx #0
// [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
// [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy
// main::@1
b1:
// i&1
// [6] (byte~) main::$1 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1
txa
and #1
// if( (i<10) && ((i&1)==0) )
// [7] if((byte) main::i#2>=(byte) $a) goto main::@2 -- vbuxx_ge_vbuc1_then_la1
cpx #$a
bcs b2
// main::@4
// [8] if((byte~) main::$1!=(byte) 0) goto main::@2 -- vbuaa_neq_0_then_la1
cmp #0
bne b2
// main::@3
// screen[i] = '*'
// [9] *((const byte*) main::screen#0 + (byte) main::i#2) ← (byte) '*' -- pbuc1_derefidx_vbuxx=vbuc2
lda #'*'
sta screen,x
// main::@2
b2:
// for( char i : 0..20)
// [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
inx
// [11] if((byte) main::i#1!=(byte) $15) goto main::@1 -- vbuxx_neq_vbuc1_then_la1
cpx #$15
bne b1
// main::@return
// }
// [12] return
rts
}
// File Data

View File

@ -0,0 +1,18 @@
(label) @1
(label) @begin
(label) @end
(void()) main()
(byte~) main::$1 reg byte a 11.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@return
(byte) main::i
(byte) main::i#1 reg byte x 16.5
(byte) main::i#2 reg byte x 11.0
(byte*) main::screen
(const byte*) main::screen#0 screen = (byte*) 1024
reg byte x [ main::i#2 main::i#1 ]
reg byte a [ main::$1 ]