diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2MultiplyToShiftRewriting.java b/src/main/java/dk/camelot64/kickc/passes/Pass2MultiplyToShiftRewriting.java index 6d9f40c39..b3d857579 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2MultiplyToShiftRewriting.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2MultiplyToShiftRewriting.java @@ -23,103 +23,140 @@ public class Pass2MultiplyToShiftRewriting extends Pass2SsaOptimization { public boolean step() { boolean optimized = false; for(ControlFlowBlock block : getGraph().getAllBlocks()) { + Scope scope = getScope().getScope(block.getScope()); ListIterator stmtIt = block.getStatements().listIterator(); while(stmtIt.hasNext()) { Statement statement = stmtIt.next(); if(statement instanceof StatementAssignment) { StatementAssignment assignment = (StatementAssignment) statement; - if(Operators.MULTIPLY.equals(assignment.getOperator()) || Operators.DIVIDE.equals(assignment.getOperator())) { - if(assignment.getrValue1() instanceof ConstantValue) continue; - ConstantLiteral constantLiteral = getConstantLiteral2(assignment); - if(constantLiteral instanceof ConstantInteger) { - Long constantInt = ((ConstantInteger) constantLiteral).getInteger(); - double power2 = Math.log(constantInt) / Math.log(2); - if(power2 == 0.0) { - // Found multiplication/division with 1 (ONE) - getLog().append("Rewriting multiplication to remove identity multiply/divide " + statement.toString(getProgram(), false)); - assignment.setOperator(null); - assignment.setrValue2(assignment.getrValue1()); - assignment.setrValue1(null); - optimized = true; - } else if(power2>0.0 && Math.round(power2)==power2 ) { - // Found a whole power of 2 - if(Operators.MULTIPLY.equals(assignment.getOperator())) { - getLog().append("Rewriting multiplication to use shift "+statement.toString(getProgram(), false)); - assignment.setOperator(Operators.SHIFT_LEFT); - assignment.setrValue2(new ConstantInteger((long)power2, SymbolType.BYTE)); - optimized = true; - } else if(Operators.DIVIDE.equals(assignment.getOperator())) { - getLog().append("Rewriting division to use shift "+statement.toString(getProgram(), false)); - assignment.setOperator(Operators.SHIFT_RIGHT); - assignment.setrValue2(new ConstantInteger((long)power2, SymbolType.BYTE)); - optimized = true; - } - } else if(Operators.MULTIPLY.equals(assignment.getOperator())) { - // Multiplication by constant - getLog().append("Rewriting multiplication to use shift and addition"+statement.toString(getProgram(), false)); - stmtIt.previous(); - Scope scope = getScope().getScope(block.getScope()); - SymbolType resultType = SymbolTypeInference.inferType(getScope(), assignment.getrValue1()); - long pow2 = (long) power2; - long remains = constantInt - (1L<=0) { - long powVal = 1L <=powVal) { - // First add shifts - Variable varShift = VariableBuilder.createIntermediate(scope, resultType, getProgram()); - stmtIt.add(new StatementAssignment((LValue) varShift.getRef(), building, Operators.SHIFT_LEFT, new ConstantInteger(shiftCount, SymbolType.BYTE), true, assignment.getSource(), Comment.NO_COMMENTS)); - shiftCount = 0; - // Then add rvalue1 - Variable varAdd = VariableBuilder.createIntermediate(scope, resultType, getProgram()); - stmtIt.add(new StatementAssignment((LValue) varAdd.getRef(), varShift.getRef(), Operators.PLUS, assignment.getrValue1(), true, assignment.getSource(), Comment.NO_COMMENTS)); - building = varAdd.getRef(); - remains -= powVal; - } - // Add a shift - if(pow2>0) { - shiftCount++; - } - pow2--; - } - // add remaining shifts - if(shiftCount>0) { - Variable varShift = VariableBuilder.createIntermediate(scope, resultType, getProgram()); - stmtIt.add(new StatementAssignment((LValue) varShift.getRef(), building, Operators.SHIFT_LEFT, new ConstantInteger(shiftCount, SymbolType.BYTE), true, assignment.getSource(), Comment.NO_COMMENTS)); - building = varShift.getRef(); - } - stmtIt.next(); - // Replace old multiplication - assignment.setOperator(null); - assignment.setrValue1(null); - assignment.setrValue2(building); - } - } + + if(Operators.MULTIPLY.equals(assignment.getOperator()) && assignment.getrValue1() instanceof ConstantValue) { + if(assignment.getrValue2() instanceof ConstantValue) continue; + final RValue varValue = assignment.getrValue2(); + final Long constValue = getConstantInteger(assignment.getrValue1()); + if(constValue == null) + continue; + optimized |= rewriteMultiply(assignment, stmtIt, constValue, varValue, scope); } + + if(Operators.MULTIPLY.equals(assignment.getOperator()) && assignment.getrValue2() instanceof ConstantValue) { + final RValue varValue = assignment.getrValue1(); + final Long constValue = getConstantInteger(assignment.getrValue2()); + if(constValue == null) + continue; + optimized |= rewriteMultiply(assignment, stmtIt, constValue, varValue, scope); + } + + if(Operators.DIVIDE.equals(assignment.getOperator()) && assignment.getrValue2() instanceof ConstantValue) { + final RValue varValue = assignment.getrValue1(); + final Long constValue = getConstantInteger(assignment.getrValue2()); + if(constValue == null) + continue; + optimized |= rewriteDivide(assignment, stmtIt, constValue, varValue, scope); + } + } } } return optimized; } + private boolean rewriteMultiply(StatementAssignment assignment, ListIterator stmtIt, Long constValue, RValue varValue, Scope scope) { + boolean optimized = false; + double power2 = Math.log(constValue) / Math.log(2); + if(power2 == 0.0) { + // Found multiplication with 1 (ONE) + getLog().append("Rewriting multiplication to remove identity multiply " + assignment.toString(getProgram(), false)); + assignment.setOperator(null); + assignment.setrValue2(varValue); + assignment.setrValue1(null); + optimized = true; + } else if(power2 > 0.0 && Math.round(power2) == power2) { + // Found a whole power of 2 + getLog().append("Rewriting multiplication to use shift " + assignment.toString(getProgram(), false)); + assignment.setOperator(Operators.SHIFT_LEFT); + assignment.setrValue2(new ConstantInteger((long) power2, SymbolType.BYTE)); + optimized = true; + } else if(Operators.MULTIPLY.equals(assignment.getOperator())) { + // Multiplication by constant + getLog().append("Rewriting multiplication to use shift and addition" + assignment.toString(getProgram(), false)); + stmtIt.previous(); + SymbolType resultType = SymbolTypeInference.inferType(getScope(), varValue); + long pow2 = (long) power2; + long remains = constValue - (1L << pow2); + RValue building = varValue; + long shiftCount = 0; + while(pow2 >= 0) { + long powVal = 1L << pow2; + if(remains >= powVal) { + // First add shifts + Variable varShift = VariableBuilder.createIntermediate(scope, resultType, getProgram()); + stmtIt.add(new StatementAssignment((LValue) varShift.getRef(), building, Operators.SHIFT_LEFT, new ConstantInteger(shiftCount, SymbolType.BYTE), true, assignment.getSource(), Comment.NO_COMMENTS)); + shiftCount = 0; + // Then add rvalue1 + Variable varAdd = VariableBuilder.createIntermediate(scope, resultType, getProgram()); + stmtIt.add(new StatementAssignment((LValue) varAdd.getRef(), varShift.getRef(), Operators.PLUS, varValue, true, assignment.getSource(), Comment.NO_COMMENTS)); + building = varAdd.getRef(); + remains -= powVal; + } + // Add a shift + if(pow2 > 0) { + shiftCount++; + } + pow2--; + } + // add remaining shifts + if(shiftCount > 0) { + Variable varShift = VariableBuilder.createIntermediate(scope, resultType, getProgram()); + stmtIt.add(new StatementAssignment((LValue) varShift.getRef(), building, Operators.SHIFT_LEFT, new ConstantInteger(shiftCount, SymbolType.BYTE), true, assignment.getSource(), Comment.NO_COMMENTS)); + building = varShift.getRef(); + } + stmtIt.next(); + // Replace old multiplication + assignment.setOperator(null); + assignment.setrValue1(null); + assignment.setrValue2(building); + } + return optimized; + } + + private boolean rewriteDivide(StatementAssignment assignment, ListIterator stmtIt, Long constValue, RValue varValue, Scope scope) { + boolean optimized = false; + double power2 = Math.log(constValue) / Math.log(2); + if(power2 == 0.0) { + // Found division with 1 (ONE) + getLog().append("Rewriting multiplication to remove identity divide " + assignment.toString(getProgram(), false)); + assignment.setOperator(null); + assignment.setrValue2(varValue); + assignment.setrValue1(null); + optimized = true; + } else if(power2 > 0.0 && Math.round(power2) == power2) { + getLog().append("Rewriting division to use shift " + assignment.toString(getProgram(), false)); + assignment.setOperator(Operators.SHIFT_RIGHT); + assignment.setrValue2(new ConstantInteger((long) power2, SymbolType.BYTE)); + optimized = true; + } + return optimized; + } + /** - * Get the constant literal value for RValue2 - or null if not possible + * Get the constant integer value for an RValue - or null if not possible * - * @param assignment The Assignment - * @return The constant literal value for RValue2 (or null) + * @param rValue The rValue + * @return The constant literal integer value (or null) */ - private ConstantLiteral getConstantLiteral2(StatementAssignment assignment) { - if(assignment.getrValue2() instanceof ConstantValue) { - ConstantValue constantValue = (ConstantValue) assignment.getrValue2(); + private Long getConstantInteger(RValue rValue) { + if(rValue instanceof ConstantValue) { + ConstantValue constantValue = (ConstantValue) rValue; try { - return constantValue.calculateLiteral(getScope()); + final ConstantLiteral constantLiteral = constantValue.calculateLiteral(getScope()); + if(constantLiteral instanceof ConstantInteger) + return ((ConstantInteger) constantLiteral).getInteger(); } catch(ConstantNotLiteral e) { return null; } - } else { - return null; } + return null; } diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 0b95783a4..c7760f1d8 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -807,6 +807,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("stars-2.c"); } + @Test + public void testMultiply4() throws IOException { + compileAndCompare("multiply-4.c"); + } + /* TODO: Add support for var*var @Test public void testMultiply3() throws IOException { diff --git a/src/test/kc/multiply-4.c b/src/test/kc/multiply-4.c new file mode 100644 index 000000000..ed552bc35 --- /dev/null +++ b/src/test/kc/multiply-4.c @@ -0,0 +1,13 @@ +// Test compile-time and run-time multiplication +// const*var multiplication - converted to shift/add + +char * const SCREEN = (char*)0x0400; + +void main() { + char i=0; + for(char c1=0;c1<5;c1++) { + // const*var + char c3 = 7*c1; + SCREEN[i++] = c3; + } +} \ No newline at end of file diff --git a/src/test/ref/multiply-4.asm b/src/test/ref/multiply-4.asm new file mode 100644 index 000000000..2ea494360 --- /dev/null +++ b/src/test/ref/multiply-4.asm @@ -0,0 +1,40 @@ +// Test compile-time and run-time multiplication +// const*var multiplication - converted to shift/add + // Commodore 64 PRG executable file +.file [name="multiply-4.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + ldy #0 + ldx #0 + __b1: + // for(char c1=0;c1<5;c1++) + cpx #5 + bcc __b2 + // } + rts + __b2: + // char c3 = 7*c1 + txa + asl + stx.z $ff + clc + adc.z $ff + asl + stx.z $ff + clc + adc.z $ff + // SCREEN[i++] = c3 + sta SCREEN,y + // SCREEN[i++] = c3; + iny + // for(char c1=0;c1<5;c1++) + inx + jmp __b1 +} diff --git a/src/test/ref/multiply-4.cfg b/src/test/ref/multiply-4.cfg new file mode 100644 index 000000000..b4b04ca92 --- /dev/null +++ b/src/test/ref/multiply-4.cfg @@ -0,0 +1,22 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [1] main::c1#2 = phi( main/0, main::@2/main::c1#1 ) + [2] if(main::c1#2<5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = main::c1#2 << 1 + [5] main::$3 = main::$2 + main::c1#2 + [6] main::$4 = main::$3 << 1 + [7] main::c3#0 = main::$4 + main::c1#2 + [8] SCREEN[main::i#2] = main::c3#0 + [9] main::i#1 = ++ main::i#2 + [10] main::c1#1 = ++ main::c1#2 + to:main::@1 diff --git a/src/test/ref/multiply-4.log b/src/test/ref/multiply-4.log new file mode 100644 index 000000000..18c3ba153 --- /dev/null +++ b/src/test/ref/multiply-4.log @@ -0,0 +1,347 @@ +Eliminating unused variable with no statement main::$1 + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + main::i#0 = 0 + main::c1#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + main::i#3 = phi( main/main::i#0, main::@2/main::i#1 ) + main::c1#2 = phi( main/main::c1#0, main::@2/main::c1#1 ) + main::$0 = main::c1#2 < 5 + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::i#2 = phi( main::@1/main::i#3 ) + main::c1#3 = phi( main::@1/main::c1#2 ) + main::c3#0 = 7 * main::c1#3 + SCREEN[main::i#2] = main::c3#0 + main::i#1 = ++ main::i#2 + main::c1#1 = ++ main::c1#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +void main() +bool main::$0 +char main::c1 +char main::c1#0 +char main::c1#1 +char main::c1#2 +char main::c1#3 +char main::c3 +char main::c3#0 +char main::i +char main::i#0 +char main::i#1 +char main::i#2 +char main::i#3 + +Adding number conversion cast (unumber) 5 in main::$0 = main::c1#2 < 5 +Adding number conversion cast (unumber) 7 in main::c3#0 = 7 * main::c1#3 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 5 +Simplifying constant integer cast 7 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 5 +Finalized unsigned number type (char) 7 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::c1#2 = main::c1#3 +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition main::$0 [4] if(main::c1#2<5) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::i#0 = 0 +Constant main::c1#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Rewriting multiplication to use shift and addition[2] main::c3#0 = 7 * main::c1#2 +Inlining constant with var siblings main::i#0 +Inlining constant with var siblings main::c1#0 +Constant inlined main::c1#0 = 0 +Constant inlined main::i#0 = 0 +Successful SSA optimization Pass2ConstantInlining +Alias main::c3#0 = main::$5 +Successful SSA optimization Pass2AliasElimination +Adding NOP phi() at start of main +CALL GRAPH + +Created 2 initial phi equivalence classes +Coalesced [11] main::c1#4 = main::c1#1 +Coalesced [12] main::i#4 = main::i#1 +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [1] main::c1#2 = phi( main/0, main::@2/main::c1#1 ) + [2] if(main::c1#2<5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = main::c1#2 << 1 + [5] main::$3 = main::$2 + main::c1#2 + [6] main::$4 = main::$3 << 1 + [7] main::c3#0 = main::$4 + main::c1#2 + [8] SCREEN[main::i#2] = main::c3#0 + [9] main::i#1 = ++ main::i#2 + [10] main::c1#1 = ++ main::c1#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$2 // 22.0 +char main::$3 // 22.0 +char main::$4 // 22.0 +char main::c1 +char main::c1#1 // 22.0 +char main::c1#2 // 8.25 +char main::c3 +char main::c3#0 // 22.0 +char main::i +char main::i#1 // 11.0 +char main::i#2 // 4.714285714285714 + +Initial phi equivalence classes +[ main::c1#2 main::c1#1 ] +[ main::i#2 main::i#1 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Added variable main::$3 to live range equivalence class [ main::$3 ] +Added variable main::$4 to live range equivalence class [ main::$4 ] +Added variable main::c3#0 to live range equivalence class [ main::c3#0 ] +Complete equivalence classes +[ main::c1#2 main::c1#1 ] +[ main::i#2 main::i#1 ] +[ main::$2 ] +[ main::$3 ] +[ main::$4 ] +[ main::c3#0 ] +Allocated zp[1]:2 [ main::c1#2 main::c1#1 ] +Allocated zp[1]:3 [ main::i#2 main::i#1 ] +Allocated zp[1]:4 [ main::$2 ] +Allocated zp[1]:5 [ main::$3 ] +Allocated zp[1]:6 [ main::$4 ] +Allocated zp[1]:7 [ main::c3#0 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] main::$2 = main::c1#2 << 1 [ main::c1#2 main::i#2 main::$2 ] ( [ main::c1#2 main::i#2 main::$2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::c1#2 main::c1#1 ] +Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::i#2 main::i#1 ] +Statement [5] main::$3 = main::$2 + main::c1#2 [ main::c1#2 main::i#2 main::$3 ] ( [ main::c1#2 main::i#2 main::$3 ] { } ) always clobbers reg byte a +Statement [6] main::$4 = main::$3 << 1 [ main::c1#2 main::i#2 main::$4 ] ( [ main::c1#2 main::i#2 main::$4 ] { } ) always clobbers reg byte a +Statement [7] main::c3#0 = main::$4 + main::c1#2 [ main::c1#2 main::i#2 main::c3#0 ] ( [ main::c1#2 main::i#2 main::c3#0 ] { } ) always clobbers reg byte a +Statement [4] main::$2 = main::c1#2 << 1 [ main::c1#2 main::i#2 main::$2 ] ( [ main::c1#2 main::i#2 main::$2 ] { } ) always clobbers reg byte a +Statement [5] main::$3 = main::$2 + main::c1#2 [ main::c1#2 main::i#2 main::$3 ] ( [ main::c1#2 main::i#2 main::$3 ] { } ) always clobbers reg byte a +Statement [6] main::$4 = main::$3 << 1 [ main::c1#2 main::i#2 main::$4 ] ( [ main::c1#2 main::i#2 main::$4 ] { } ) always clobbers reg byte a +Statement [7] main::c3#0 = main::$4 + main::c1#2 [ main::c1#2 main::i#2 main::c3#0 ] ( [ main::c1#2 main::i#2 main::c3#0 ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ main::c1#2 main::c1#1 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::i#2 main::i#1 ] : zp[1]:3 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ main::$2 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::$3 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ main::$4 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ main::c3#0 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 30.25: zp[1]:2 [ main::c1#2 main::c1#1 ] 22: zp[1]:4 [ main::$2 ] 22: zp[1]:5 [ main::$3 ] 22: zp[1]:6 [ main::$4 ] 22: zp[1]:7 [ main::c3#0 ] 15.71: zp[1]:3 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 641 combination reg byte x [ main::c1#2 main::c1#1 ] reg byte a [ main::$2 ] reg byte a [ main::$3 ] reg byte a [ main::$4 ] zp[1]:7 [ main::c3#0 ] zp[1]:3 [ main::i#2 main::i#1 ] +Limited combination testing to 100 combinations of 2304 possible. +Uplifting [] best 641 combination +Attempting to uplift remaining variables inzp[1]:7 [ main::c3#0 ] +Uplifting [main] best 581 combination reg byte a [ main::c3#0 ] +Attempting to uplift remaining variables inzp[1]:3 [ main::i#2 main::i#1 ] +Uplifting [main] best 491 combination reg byte y [ main::i#2 main::i#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test compile-time and run-time multiplication +// const*var multiplication - converted to shift/add + // Upstart + // Commodore 64 PRG executable file +.file [name="multiply-4.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + // [1] phi main::c1#2 = 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // main::@1 + __b1: + // [2] if(main::c1#2<5) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #5 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] main::$2 = main::c1#2 << 1 -- vbuaa=vbuxx_rol_1 + txa + asl + // [5] main::$3 = main::$2 + main::c1#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + // [6] main::$4 = main::$3 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [7] main::c3#0 = main::$4 + main::c1#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + // [8] SCREEN[main::i#2] = main::c3#0 -- pbuc1_derefidx_vbuyy=vbuaa + sta SCREEN,y + // [9] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // [10] main::c1#1 = ++ main::c1#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy + // [1] phi main::c1#2 = main::c1#1 [phi:main::@2->main::@1#1] -- register_copy + jmp __b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$2 // reg byte a 22.0 +char main::$3 // reg byte a 22.0 +char main::$4 // reg byte a 22.0 +char main::c1 +char main::c1#1 // reg byte x 22.0 +char main::c1#2 // reg byte x 8.25 +char main::c3 +char main::c3#0 // reg byte a 22.0 +char main::i +char main::i#1 // reg byte y 11.0 +char main::i#2 // reg byte y 4.714285714285714 + +reg byte x [ main::c1#2 main::c1#1 ] +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$3 ] +reg byte a [ main::$4 ] +reg byte a [ main::c3#0 ] + + +FINAL ASSEMBLER +Score: 431 + + // File Comments +// Test compile-time and run-time multiplication +// const*var multiplication - converted to shift/add + // Upstart + // Commodore 64 PRG executable file +.file [name="multiply-4.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + // [1] phi main::c1#2 = 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + ldx #0 + // main::@1 + __b1: + // for(char c1=0;c1<5;c1++) + // [2] if(main::c1#2<5) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #5 + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // char c3 = 7*c1 + // [4] main::$2 = main::c1#2 << 1 -- vbuaa=vbuxx_rol_1 + txa + asl + // [5] main::$3 = main::$2 + main::c1#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + // [6] main::$4 = main::$3 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [7] main::c3#0 = main::$4 + main::c1#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + // SCREEN[i++] = c3 + // [8] SCREEN[main::i#2] = main::c3#0 -- pbuc1_derefidx_vbuyy=vbuaa + sta SCREEN,y + // SCREEN[i++] = c3; + // [9] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // for(char c1=0;c1<5;c1++) + // [10] main::c1#1 = ++ main::c1#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy + // [1] phi main::c1#2 = main::c1#1 [phi:main::@2->main::@1#1] -- register_copy + jmp __b1 +} + // File Data + diff --git a/src/test/ref/multiply-4.sym b/src/test/ref/multiply-4.sym new file mode 100644 index 000000000..b246e22ad --- /dev/null +++ b/src/test/ref/multiply-4.sym @@ -0,0 +1,20 @@ +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$2 // reg byte a 22.0 +char main::$3 // reg byte a 22.0 +char main::$4 // reg byte a 22.0 +char main::c1 +char main::c1#1 // reg byte x 22.0 +char main::c1#2 // reg byte x 8.25 +char main::c3 +char main::c3#0 // reg byte a 22.0 +char main::i +char main::i#1 // reg byte y 11.0 +char main::i#2 // reg byte y 4.714285714285714 + +reg byte x [ main::c1#2 main::c1#1 ] +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$3 ] +reg byte a [ main::$4 ] +reg byte a [ main::c3#0 ]