1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-26 18:29:54 +00:00

Fixed multiplication rewriting to shift/add to also support const*var. Closes #201

This commit is contained in:
jespergravgaard 2021-08-15 00:47:48 +02:00
parent 31ecd6a2c5
commit db4fe786a7
7 changed files with 561 additions and 77 deletions

View File

@ -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<Statement> 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<<pow2);
RValue building = assignment.getrValue1();
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, 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<Statement> 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<Statement> 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;
}

View File

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

13
src/test/kc/multiply-4.c Normal file
View File

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

View File

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

View File

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

347
src/test/ref/multiply-4.log Normal file
View File

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

View File

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