mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-27 19:50:10 +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:
commit
c88932d423
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
@ -472,7 +476,7 @@ public interface ProgramValue {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void set(Value value) {
|
public void set(Value value) {
|
||||||
structValue.setValue(memberRef, (ConstantValue)value);
|
structValue.setValue(memberRef, (ConstantValue) value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
@ -50,9 +52,12 @@ public class Pass2ConstantIfs extends Pass2SsaOptimization {
|
|||||||
literal = constVal.calculateLiteral(getScope());
|
literal = constVal.calculateLiteral(getScope());
|
||||||
} else if(constValue1 != null && operator != null && constValue2 != null) {
|
} else if(constValue1 != null && operator != null && constValue2 != null) {
|
||||||
// Constant binary condition
|
// Constant binary condition
|
||||||
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()) {
|
||||||
|
@ -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;
|
||||||
|
@ -140,7 +140,7 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProgram().clearVariableReferenceInfos();
|
getProgram().clearVariableReferenceInfos();
|
||||||
new PassNStatementIndices(getProgram()).clearStatementIndices();
|
getProgram().clearStatementIndices();
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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<>();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
12
src/test/kc/bool-ifs-min.kc
Normal file
12
src/test/kc/bool-ifs-min.kc
Normal 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] = '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
22
src/test/ref/bool-ifs-min.asm
Normal file
22
src/test/ref/bool-ifs-min.asm
Normal 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
|
||||||
|
}
|
30
src/test/ref/bool-ifs-min.cfg
Normal file
30
src/test/ref/bool-ifs-min.cfg
Normal 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
|
458
src/test/ref/bool-ifs-min.log
Normal file
458
src/test/ref/bool-ifs-min.log
Normal 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
|
||||||
|
|
18
src/test/ref/bool-ifs-min.sym
Normal file
18
src/test/ref/bool-ifs-min.sym
Normal 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 ]
|
Loading…
Reference in New Issue
Block a user