mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-08-09 04:25:12 +00:00
Fixed multiplication rewriting to shift/add to also support const*var. Closes #201
This commit is contained in:
@@ -23,103 +23,140 @@ public class Pass2MultiplyToShiftRewriting extends Pass2SsaOptimization {
|
|||||||
public boolean step() {
|
public boolean step() {
|
||||||
boolean optimized = false;
|
boolean optimized = false;
|
||||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
Scope scope = getScope().getScope(block.getScope());
|
||||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
while(stmtIt.hasNext()) {
|
while(stmtIt.hasNext()) {
|
||||||
Statement statement = stmtIt.next();
|
Statement statement = stmtIt.next();
|
||||||
if(statement instanceof StatementAssignment) {
|
if(statement instanceof StatementAssignment) {
|
||||||
StatementAssignment assignment = (StatementAssignment) statement;
|
StatementAssignment assignment = (StatementAssignment) statement;
|
||||||
if(Operators.MULTIPLY.equals(assignment.getOperator()) || Operators.DIVIDE.equals(assignment.getOperator())) {
|
|
||||||
if(assignment.getrValue1() instanceof ConstantValue) continue;
|
if(Operators.MULTIPLY.equals(assignment.getOperator()) && assignment.getrValue1() instanceof ConstantValue) {
|
||||||
ConstantLiteral constantLiteral = getConstantLiteral2(assignment);
|
if(assignment.getrValue2() instanceof ConstantValue) continue;
|
||||||
if(constantLiteral instanceof ConstantInteger) {
|
final RValue varValue = assignment.getrValue2();
|
||||||
Long constantInt = ((ConstantInteger) constantLiteral).getInteger();
|
final Long constValue = getConstantInteger(assignment.getrValue1());
|
||||||
double power2 = Math.log(constantInt) / Math.log(2);
|
if(constValue == null)
|
||||||
if(power2 == 0.0) {
|
continue;
|
||||||
// Found multiplication/division with 1 (ONE)
|
optimized |= rewriteMultiply(assignment, stmtIt, constValue, varValue, scope);
|
||||||
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.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;
|
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
|
* @param rValue The rValue
|
||||||
* @return The constant literal value for RValue2 (or null)
|
* @return The constant literal integer value (or null)
|
||||||
*/
|
*/
|
||||||
private ConstantLiteral getConstantLiteral2(StatementAssignment assignment) {
|
private Long getConstantInteger(RValue rValue) {
|
||||||
if(assignment.getrValue2() instanceof ConstantValue) {
|
if(rValue instanceof ConstantValue) {
|
||||||
ConstantValue constantValue = (ConstantValue) assignment.getrValue2();
|
ConstantValue constantValue = (ConstantValue) rValue;
|
||||||
try {
|
try {
|
||||||
return constantValue.calculateLiteral(getScope());
|
final ConstantLiteral constantLiteral = constantValue.calculateLiteral(getScope());
|
||||||
|
if(constantLiteral instanceof ConstantInteger)
|
||||||
|
return ((ConstantInteger) constantLiteral).getInteger();
|
||||||
} catch(ConstantNotLiteral e) {
|
} catch(ConstantNotLiteral e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -807,6 +807,11 @@ public class TestProgramsFast extends TestPrograms {
|
|||||||
compileAndCompare("stars-2.c");
|
compileAndCompare("stars-2.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiply4() throws IOException {
|
||||||
|
compileAndCompare("multiply-4.c");
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Add support for var*var
|
/* TODO: Add support for var*var
|
||||||
@Test
|
@Test
|
||||||
public void testMultiply3() throws IOException {
|
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 ]
|
Reference in New Issue
Block a user