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

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

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

View File

@ -31,6 +31,9 @@ public class Compiler {
/** Enable the zero-page coalesce pass. It takes a lot of time, but limits the zero page usage significantly. */
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;
}

View File

@ -66,6 +66,9 @@ public class KickC implements Callable<Void> {
@CommandLine.Option(names = {"-Ocoalesce"}, description = "Optimization Option. Enables zero-page coalesce pass which limits zero-page usage significantly, but takes a lot of compile time.")
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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,10 +6,7 @@ import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.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());
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -10,8 +10,11 @@ import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.VariableVersion;
import dk.camelot64.kickc.model.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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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