mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-23 23:32:55 +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. */
|
||||
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() {
|
||||
this.program = new Program();
|
||||
}
|
||||
@ -43,6 +46,10 @@ public class Compiler {
|
||||
this.enableZeroPageCoalasce = true;
|
||||
}
|
||||
|
||||
void enableLoopHeadConstant() {
|
||||
this.enableLoopHeadConstant = true;
|
||||
}
|
||||
|
||||
void setTargetPlatform(TargetPlatform targetPlatform) {
|
||||
program.setTargetPlatform(targetPlatform);
|
||||
}
|
||||
@ -305,10 +312,13 @@ public class Compiler {
|
||||
optimizations.add(new PassNSimplifyExpressionWithZero(program));
|
||||
optimizations.add(new PassNEliminateUnusedVars(program, true));
|
||||
optimizations.add(new Pass2EliminateUnusedBlocks(program));
|
||||
optimizations.add(new PassNStatementIndices(program));
|
||||
optimizations.add(() -> { program.clearDominators(); return false; });
|
||||
optimizations.add(() -> { program.clearLoopSet(); return false; });
|
||||
optimizations.add(new Pass2LoopHeadConstantIdentification(program));
|
||||
if(enableLoopHeadConstant) {
|
||||
optimizations.add(new PassNStatementIndices(program));
|
||||
optimizations.add(() -> { program.clearDominators(); return false; });
|
||||
optimizations.add(() -> { program.clearLoopSet(); return false; });
|
||||
optimizations.add(new Pass2LoopHeadConstantIdentification(program));
|
||||
optimizations.add(() -> { program.clearStatementIndices(); return false; });
|
||||
}
|
||||
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.")
|
||||
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.")
|
||||
private boolean optimizeFragmentCache = false;
|
||||
|
||||
@ -212,6 +215,10 @@ public class KickC implements Callable<Void> {
|
||||
compiler.enableZeroPageCoalasce();
|
||||
}
|
||||
|
||||
if(optimizeLoopHeadConstant) {
|
||||
compiler.enableLoopHeadConstant();
|
||||
}
|
||||
|
||||
System.out.println("Compiling " + kcFile);
|
||||
Program program = null;
|
||||
try {
|
||||
|
@ -2,6 +2,7 @@ package dk.camelot64.kickc.model;
|
||||
|
||||
import dk.camelot64.kickc.CompileLog;
|
||||
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.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
@ -302,6 +303,17 @@ public class Program {
|
||||
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() {
|
||||
if(symbolInfos==null)
|
||||
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}.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public interface ProgramValue {
|
||||
|
||||
@ -204,6 +203,11 @@ public interface ProgramValue {
|
||||
public void set(Value value) {
|
||||
phiVariable.getValues().get(i).setrValue((RValue) value);
|
||||
}
|
||||
|
||||
public LabelRef getPredecessor() {
|
||||
return phiVariable.getValues().get(i).getPredecessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PhiValuePredecessor implements ProgramValue {
|
||||
@ -472,7 +476,7 @@ public interface ProgramValue {
|
||||
|
||||
@Override
|
||||
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.types.SymbolType;
|
||||
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 java.util.Objects;
|
||||
@ -17,8 +17,8 @@ public class OperatorEqual extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(Objects.equals(((ConstantInteger) left).getInteger(), ((ConstantInteger) right).getInteger()));
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(Objects.equals(((ConstantEnumerable) left).getInteger(), ((ConstantEnumerable) right).getInteger()));
|
||||
}
|
||||
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.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.ConstantBool;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ConstantLiteral;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
/** Binary greater-than Operator ( x > y ) */
|
||||
public class OperatorGreaterThan extends OperatorBinary {
|
||||
@ -15,8 +13,8 @@ public class OperatorGreaterThan extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(((ConstantInteger) left).getInteger() > ((ConstantInteger) right).getInteger());
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(((ConstantEnumerable) left).getInteger() > ((ConstantEnumerable) right).getInteger());
|
||||
}
|
||||
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.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.ConstantBool;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ConstantLiteral;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
/** Binary greater-than-equal Operator ( x >= y ) */
|
||||
public class OperatorGreaterThanEqual extends OperatorBinary {
|
||||
@ -15,8 +13,8 @@ public class OperatorGreaterThanEqual extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(((ConstantInteger) left).getInteger() >= ((ConstantInteger) right).getInteger());
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(((ConstantEnumerable) left).getInteger() >= ((ConstantEnumerable) right).getInteger());
|
||||
}
|
||||
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.types.SymbolType;
|
||||
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;
|
||||
|
||||
/** Binary less-than Operator ( x < y ) */
|
||||
@ -15,8 +15,8 @@ public class OperatorLessThan extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(((ConstantInteger) left).getInteger() < ((ConstantInteger) right).getInteger());
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(((ConstantEnumerable) left).getInteger() < ((ConstantEnumerable) right).getInteger());
|
||||
}
|
||||
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.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.ConstantBool;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ConstantLiteral;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
/** Binary less-than-equal Operator ( x <= y ) */
|
||||
public class OperatorLessThanEqual extends OperatorBinary {
|
||||
@ -15,8 +13,8 @@ public class OperatorLessThanEqual extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(((ConstantInteger) left).getInteger() <= ((ConstantInteger) right).getInteger());
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(((ConstantEnumerable) left).getInteger() <= ((ConstantEnumerable) right).getInteger());
|
||||
}
|
||||
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.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.ConstantBool;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ConstantLiteral;
|
||||
import dk.camelot64.kickc.model.values.ConstantPointer;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -18,10 +15,8 @@ public class OperatorNotEqual extends OperatorBinary {
|
||||
|
||||
@Override
|
||||
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
|
||||
if(left instanceof ConstantInteger && right instanceof ConstantInteger) {
|
||||
return new ConstantBool(!Objects.equals(((ConstantInteger) left).getInteger(), ((ConstantInteger) right).getInteger()));
|
||||
} else if(left instanceof ConstantPointer && right instanceof ConstantPointer) {
|
||||
return new ConstantBool(!Objects.equals(((ConstantPointer) left).getLocation(), ((ConstantPointer) right).getLocation()));
|
||||
if(left instanceof ConstantEnumerable && right instanceof ConstantEnumerable) {
|
||||
return new ConstantBool(!Objects.equals(((ConstantEnumerable) left).getInteger(), ((ConstantEnumerable) right).getInteger()));
|
||||
}
|
||||
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
|
||||
}
|
||||
|
@ -945,16 +945,32 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
BlockScope blockScope = getCurrentScope().addBlockScope();
|
||||
scopeStack.push(blockScope);
|
||||
loopStack.push(new Loop(blockScope));
|
||||
// Add initialization
|
||||
this.visit(ctx.forClassicInit());
|
||||
KickCParser.StmtForContext stmtForCtx = (KickCParser.StmtForContext) ctx.getParent();
|
||||
// Add label
|
||||
Label repeatLabel = getCurrentScope().addLabelIntermediate();
|
||||
Label beginJumpLabel = getCurrentScope().addLabelIntermediate();
|
||||
Label doJumpLabel = getCurrentScope().addLabelIntermediate();
|
||||
Label endJumpLabel = getCurrentScope().addLabelIntermediate();
|
||||
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);
|
||||
// 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
|
||||
addLoopBody(stmtForCtx.stmt());
|
||||
addLoopContinueLabel(loopStack.peek(), ctx);
|
||||
// Add increment
|
||||
KickCParser.CommaExprContext incrementCtx = ctx.commaExpr(1);
|
||||
if(incrementCtx != null) {
|
||||
@ -962,14 +978,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
this.visit(incrementCtx);
|
||||
PrePostModifierHandler.addPostModifiers(this, incrementCtx, StatementSource.forClassic(ctx));
|
||||
}
|
||||
// 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, repeatLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
|
||||
sequence.addStatement(doJmpStmt);
|
||||
// Jump back to beginning
|
||||
Statement beginJmpStmt = new StatementJump(beginJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
|
||||
sequence.addStatement(beginJmpStmt);
|
||||
StatementLabel endJumpTarget = new StatementLabel(endJumpLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS);
|
||||
sequence.addStatement(endJumpTarget);
|
||||
addDirectives(doJmpStmt, stmtForCtx.directive());
|
||||
addLoopBreakLabel(loopStack.pop(), ctx);
|
||||
scopeStack.pop();
|
||||
|
@ -39,7 +39,7 @@ public class Pass1AssertUsedVars extends Pass1Base {
|
||||
ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME));
|
||||
assertUsedVars(beginBlock, null, referenceInfos, new LinkedHashSet<>(), new LinkedHashSet<>());
|
||||
getProgram().clearVariableReferenceInfos();
|
||||
new PassNStatementIndices(getProgram()).clearStatementIndices();
|
||||
getProgram().clearStatementIndices();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.ConstantNotLiteral;
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.operators.Operator;
|
||||
@ -41,17 +42,21 @@ public class Pass2ConstantIfs extends Pass2SsaOptimization {
|
||||
ConstantValue constValue1 = Pass2ConstantIdentification.getConstant(conditional.getrValue1());
|
||||
Operator operator = conditional.getOperator();
|
||||
ConstantValue constValue2 = Pass2ConstantIdentification.getConstant(conditional.getrValue2());
|
||||
if(conditional.getrValue1() == null && operator == null && constValue2 != null) {
|
||||
// Constant condition
|
||||
literal = constValue2.calculateLiteral(getScope());
|
||||
} else if(conditional.getrValue1() == null && operator != null && constValue2 != null) {
|
||||
// Constant unary condition
|
||||
ConstantValue constVal = Pass2ConstantIdentification.createUnary((OperatorUnary) operator, constValue2);
|
||||
literal = constVal.calculateLiteral(getScope());
|
||||
} else if(constValue1 != null && operator != null && constValue2 != null) {
|
||||
// Constant binary condition
|
||||
ConstantValue constVal = Pass2ConstantIdentification.createBinary( constValue1, (OperatorBinary) operator, constValue2, getScope());
|
||||
literal = constVal.calculateLiteral(getScope());
|
||||
try {
|
||||
if(conditional.getrValue1() == null && operator == null && constValue2 != null) {
|
||||
// Constant condition
|
||||
literal = constValue2.calculateLiteral(getScope());
|
||||
} else if(conditional.getrValue1() == null && operator != null && constValue2 != null) {
|
||||
// Constant unary condition
|
||||
ConstantValue constVal = Pass2ConstantIdentification.createUnary((OperatorUnary) operator, constValue2);
|
||||
literal = constVal.calculateLiteral(getScope());
|
||||
} else if(constValue1 != null && operator != null && constValue2 != null) {
|
||||
// Constant binary condition
|
||||
ConstantValue constVal = Pass2ConstantIdentification.createBinary(constValue1, (OperatorBinary) operator, constValue2, getScope());
|
||||
literal = constVal.calculateLiteral(getScope());
|
||||
}
|
||||
} catch (ConstantNotLiteral e) {
|
||||
// not literal - keep as null
|
||||
}
|
||||
if(literal!=null && literal instanceof ConstantBool) {
|
||||
// Condition is a constant boolean
|
||||
|
@ -6,10 +6,7 @@ import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
||||
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.values.ConstantValue;
|
||||
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.model.values.*;
|
||||
import dk.camelot64.kickc.passes.utils.Unroller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -34,10 +31,9 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
|
||||
for(NaturalLoop loop : loopSet.getLoops()) {
|
||||
LabelRef loopHeadRef = loop.getHead();
|
||||
ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHeadRef);
|
||||
//TODO: Fix remaining errors!
|
||||
//boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos);
|
||||
boolean modified = false;
|
||||
boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos);
|
||||
if(modified) {
|
||||
getProgram().clearVariableReferenceInfos();
|
||||
getProgram().clearStatementInfos();
|
||||
getProgram().clearLoopSet();
|
||||
getProgram().clearDominators();
|
||||
@ -45,7 +41,7 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
|
||||
}
|
||||
}
|
||||
// TODO: Move to Program
|
||||
new PassNStatementIndices(getProgram()).clearStatementIndices();
|
||||
getProgram().clearStatementIndices();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -74,8 +70,19 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
|
||||
// Predecessor it outside the loop
|
||||
if(value.getrValue() instanceof ConstantValue) {
|
||||
// The value is constant in the predecessor!!
|
||||
// Optimization of the loop head is a good idea for this variable!
|
||||
optimizeVars.add(phiVariable.getVariable());
|
||||
// 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!
|
||||
optimizeVars.add(phiVariable.getVariable());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
||||
}
|
||||
|
||||
getProgram().clearVariableReferenceInfos();
|
||||
new PassNStatementIndices(getProgram()).clearStatementIndices();
|
||||
getProgram().clearStatementIndices();
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
@ -29,16 +29,4 @@ public class PassNStatementIndices extends Pass2SsaOptimization {
|
||||
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
|
||||
* @return Variables defined by the statement
|
||||
*/
|
||||
private Collection<VariableRef> getDefinedVars(Statement stmt) {
|
||||
if(stmt instanceof StatementAssignment) {
|
||||
StatementAssignment assignment = (StatementAssignment) stmt;
|
||||
LValue lValue = assignment.getlValue();
|
||||
if(lValue instanceof VariableRef) {
|
||||
return Collections.singletonList((VariableRef) lValue);
|
||||
}
|
||||
} else if(stmt instanceof StatementPhiBlock) {
|
||||
public static Collection<VariableRef> getDefinedVars(Statement stmt) {
|
||||
if(stmt instanceof StatementPhiBlock) {
|
||||
List<VariableRef> defined = new ArrayList<>();
|
||||
StatementPhiBlock phi = (StatementPhiBlock) stmt;
|
||||
for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {
|
||||
defined.add(phiVariable.getVariable());
|
||||
}
|
||||
return defined;
|
||||
} else if(stmt instanceof StatementCall) {
|
||||
List<VariableRef> defined = new ArrayList<>();
|
||||
if(((StatementCall) stmt).getlValue() instanceof VariableRef) {
|
||||
defined.add((VariableRef) ((StatementCall) stmt).getlValue());
|
||||
} else if(stmt instanceof StatementLValue) {
|
||||
LValue lValue = ((StatementLValue) stmt).getlValue();
|
||||
if(lValue instanceof VariableRef) {
|
||||
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<>();
|
||||
}
|
||||
|
@ -10,8 +10,11 @@ import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.symbols.VariableVersion;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
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.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Utility for copying blocks in a program - typically to unroll loops or conditions.
|
||||
@ -57,12 +60,10 @@ public class Unroller {
|
||||
public void unroll() {
|
||||
// 0. Prepare for copying by ensuring that all variables defined in the blocks are represented in PHI-blocks of the successors
|
||||
prepare();
|
||||
|
||||
if(program.getLog().isVerboseSSAOptimize()) {
|
||||
program.getLog().append("CONTROL FLOW GRAPH (PREPARED)");
|
||||
if(program.getLog().isVerboseLoopUnroll()) {
|
||||
program.getLog().append("CONTROL FLOW GRAPH (PREPARED FOR LOOP HEAD UNROLL)");
|
||||
program.getLog().append(program.getGraph().toString(program));
|
||||
}
|
||||
|
||||
// 1. Create new versions of all symbols assigned inside the loop
|
||||
this.varsOriginalToCopied = copyDefinedVars(unrollBlocks, program);
|
||||
// 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() {
|
||||
for(VariableRef origVarRef : getVarsDefinedIn(unrollBlocks, program)) {
|
||||
// Find out if the variable is ever referenced outside the loop
|
||||
if(isReferencedOutside(origVarRef, unrollBlocks, program)) {
|
||||
// Add any needed PHI-statements to the successors
|
||||
for(SuccessorTransition successorTransition : getSuccessorTransitions(unrollBlocks, program.getGraph())) {
|
||||
ControlFlowBlock successorBlock = program.getGraph().getBlock(successorTransition.successor);
|
||||
StatementPhiBlock phiBlock = successorBlock.getPhiBlock();
|
||||
// Create a new version of the variable
|
||||
Variable origVar = program.getScope().getVariable(origVarRef);
|
||||
Variable newVar;
|
||||
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));
|
||||
|
||||
// Re-version all usages of the specific variable version
|
||||
Map<LabelRef, VariableRef> newPhis = new LinkedHashMap<>();
|
||||
Map<LabelRef, VariableRef> varVersions = new LinkedHashMap<>();
|
||||
reVersionAllUsages(origVarRef, newPhis, varVersions);
|
||||
if(program.getLog().isVerboseLoopUnroll()) {
|
||||
program.getLog().append("Created new versions for " + origVarRef + ")");
|
||||
//program.getLog().append(program.getGraph().toString(program));
|
||||
}
|
||||
// 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.
|
||||
* @param blockRef The block to replace the usage from
|
||||
* @param origVarRef The original variable
|
||||
* @param newVarRef The new variable replacing the original
|
||||
* @param visited All blocks that have already been visited.
|
||||
* Find all usages of a variable and create new versions for each usage.
|
||||
*
|
||||
* @param origVarRef The original variable where all usages must have new versions
|
||||
* @param newPhis Map that will be populated with all new (empty) PHI-variables for the new versionw - these will be populated later.
|
||||
* @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) {
|
||||
VariableReferenceInfos variableReferenceInfos = program.getVariableReferenceInfos();
|
||||
LinkedHashMap<SymbolRef, RValue> aliases = new LinkedHashMap<>();
|
||||
aliases.put(origVarRef, newVarRef);
|
||||
AliasReplacer aliasReplacer = new AliasReplacer(aliases);
|
||||
ControlFlowBlock block = program.getGraph().getBlock(blockRef);
|
||||
if(block!=null) {
|
||||
private void reVersionAllUsages(VariableRef origVarRef, Map<LabelRef, VariableRef> newPhis, Map<LabelRef, VariableRef> varVersions) {
|
||||
|
||||
// First add the definition of origVar to varVersions
|
||||
for(ControlFlowBlock block : program.getGraph().getAllBlocks()) {
|
||||
for(Statement statement : block.getStatements()) {
|
||||
Collection<VariableRef> definedVars = variableReferenceInfos.getDefinedVars(statement);
|
||||
if(definedVars!=null && definedVars.contains(origVarRef)) {
|
||||
// Found definition of the original variable - don't replace any more
|
||||
return;
|
||||
Collection<VariableRef> definedVars = PassNCalcVariableReferenceInfos.getDefinedVars(statement);
|
||||
if(definedVars.contains(origVarRef)) {
|
||||
varVersions.put(block.getLabel(), origVarRef);
|
||||
}
|
||||
// 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);
|
||||
// Next iterate the entire graph ensuring that all usages create new versions (except usages right after the definition)
|
||||
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);
|
||||
}
|
||||
if(block.getDefaultSuccessor() != null && !visited.contains(block.getDefaultSuccessor())) {
|
||||
forwardReplaceAllUsages(block.getDefaultSuccessor(), origVarRef, newVarRef, visited);
|
||||
}
|
||||
if(block.getCallSuccessor() != null && !visited.contains(block.getCallSuccessor())) {
|
||||
forwardReplaceAllUsages(block.getCallSuccessor(), origVarRef, newVarRef, visited);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 origBlock The label of the original block
|
||||
|
@ -1952,8 +1952,8 @@ public class TestPrograms {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolIfs() throws IOException, URISyntaxException {
|
||||
compileAndCompare("bool-ifs");
|
||||
public void testBoolIfsMin() throws IOException, URISyntaxException {
|
||||
compileAndCompare("bool-ifs-min");
|
||||
}
|
||||
|
||||
@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