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:
parent
31ecd6a2c5
commit
db4fe786a7
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
13
src/test/kc/multiply-4.c
Normal 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;
|
||||
}
|
||||
}
|
40
src/test/ref/multiply-4.asm
Normal file
40
src/test/ref/multiply-4.asm
Normal 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
|
||||
}
|
22
src/test/ref/multiply-4.cfg
Normal file
22
src/test/ref/multiply-4.cfg
Normal 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
347
src/test/ref/multiply-4.log
Normal 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
|
||||
|
20
src/test/ref/multiply-4.sym
Normal file
20
src/test/ref/multiply-4.sym
Normal 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 ]
|
Loading…
Reference in New Issue
Block a user