diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java index 571f30363..340bfd50f 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java @@ -40,6 +40,10 @@ public class AsmFormat { operator.getOperator() + getAsmConstant(program, binary.getRight(), operator.getPrecedence(), codeScope) + (parenthesis ? ")" : ""); + } else if(value instanceof ConstantVarPointer) { + VariableRef toVar = ((ConstantVarPointer) value).getToVar(); + Variable variable = program.getScope().getVariable(toVar); + return getAsmParamName(variable, codeScope); } else { throw new RuntimeException("Constant type not supported " + value); } diff --git a/src/main/java/dk/camelot64/kickc/model/ConstantVarPointer.java b/src/main/java/dk/camelot64/kickc/model/ConstantVarPointer.java new file mode 100644 index 000000000..c0ecbbeca --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/ConstantVarPointer.java @@ -0,0 +1,31 @@ +package dk.camelot64.kickc.model; + +/** A pointer to a variable */ +public class ConstantVarPointer implements ConstantValue { + + /** The variable pointed to. */ + private VariableRef toVar; + + public ConstantVarPointer(VariableRef toVar) { + this.toVar = toVar; + } + + public VariableRef getToVar() { + return toVar; + } + + public void setToVar(VariableRef toVar) { + this.toVar = toVar; + } + + @Override + public SymbolType getType(ProgramScope scope) { + Variable to = scope.getVariable(toVar); + return new SymbolTypePointer(to.getType()); + } + + @Override + public String toString(Program program) { + return "&" + toVar.toString(program); + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/Operator.java b/src/main/java/dk/camelot64/kickc/model/Operator.java index b4463f8f3..840841447 100644 --- a/src/main/java/dk/camelot64/kickc/model/Operator.java +++ b/src/main/java/dk/camelot64/kickc/model/Operator.java @@ -12,6 +12,7 @@ public class Operator { public static final Operator BOOL_NOT = new Operator("~", "_not_", Type.UNARY, 2); public static final Operator LOGIC_NOT = new Operator("!", "_not_", Type.UNARY, 2); public static final Operator DEREF = new Operator("*", "_deref_", Type.UNARY, 2); + public static final Operator ADDRESS_OF = new Operator("&", "_addr_", Type.UNARY, 2); public static final Operator WORD = new Operator("w=", "_word_", Type.BINARY, 2); public static final Operator DEREF_IDX = new Operator("*idx", "_derefidx_", Type.BINARY, 2); public static final Operator SET_LOWBYTE = new Operator("lo=", "_setlo_", Type.BINARY, 2); @@ -118,6 +119,8 @@ public class Operator { return LOWBYTE; case ">": return HIBYTE; + case "&": + return ADDRESS_OF; default: throw new RuntimeException("Unknown operator " + op); } diff --git a/src/main/java/dk/camelot64/kickc/model/SymbolTypeInference.java b/src/main/java/dk/camelot64/kickc/model/SymbolTypeInference.java index d54da5811..bcc91c101 100644 --- a/src/main/java/dk/camelot64/kickc/model/SymbolTypeInference.java +++ b/src/main/java/dk/camelot64/kickc/model/SymbolTypeInference.java @@ -32,6 +32,9 @@ public class SymbolTypeInference { return SymbolType.SWORD; } else if(operator.equals(Operator.CAST_PTRBY)) { return new SymbolTypePointer(SymbolType.BYTE); + } else if(operator.equals(Operator.ADDRESS_OF)) { + SymbolType valueType = inferType(programScope, rValue); + return new SymbolTypePointer(valueType); } if(rValue instanceof ConstantValue) { ConstantValue value = ConstantValueCalculator.calcValue(programScope, operator, (ConstantValue) rValue); @@ -323,6 +326,8 @@ public class SymbolTypeInference { return new SymbolTypeArray(((ConstantArrayList) rValue).getElementType()); } else if(rValue instanceof ConstantArrayFilled) { return new SymbolTypeArray(((ConstantArrayFilled) rValue).getElementType(), ((ConstantArrayFilled) rValue).getSize()); + } else if(rValue instanceof ConstantVarPointer) { + return ((ConstantVarPointer) rValue).getType(symbols); } else if(rValue instanceof CastValue) { return ((CastValue) rValue).getToType(); } diff --git a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java index 124006449..08740575b 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java @@ -9,9 +9,6 @@ import java.util.Map; /** Cached information about which variables are defined/referenced/used in statements / blocks. */ public class VariableReferenceInfos { - /** The congtaining program. */ - private Program program; - /** Variables referenced in each block. */ private Map> blockReferencedVars; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index 367e39b22..e221477c2 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -158,6 +158,11 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { } } } + } else if(Operator.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1()==null) { + if(assignment.getrValue2() instanceof VariableRef) { + ConstantVarPointer constantVarPointer = new ConstantVarPointer((VariableRef) assignment.getrValue2()); + constants.put(variable, constantVarPointer); + } } } return null; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java index bc9f50ede..a6d5247a7 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java @@ -2,7 +2,9 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.*; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; /*** Initialize potential registers for each equivalence class. @@ -40,19 +42,41 @@ public class Pass4RegisterUpliftPotentialInitialize extends Pass2Base { } else { Registers.Register defaultRegister = equivalenceClass.getRegister(); Registers.RegisterType registerType = defaultRegister.getType(); - if(registerType.equals(Registers.RegisterType.ZP_BYTE)) { - List potentials = Arrays.asList( - defaultRegister, - Registers.getRegisterA(), - Registers.getRegisterX(), - Registers.getRegisterY()); - registerPotentials.setPotentialRegisters(equivalenceClass, potentials); - } else { - registerPotentials.setPotentialRegisters(equivalenceClass, Arrays.asList(defaultRegister)); + List potentials = new ArrayList<>(); + potentials.add(defaultRegister); + if(registerType.equals(Registers.RegisterType.ZP_BYTE) && !varRefExtracted(equivalenceClass)) { + potentials.add(Registers.getRegisterA()); + potentials.add(Registers.getRegisterX()); + potentials.add(Registers.getRegisterY()); } + registerPotentials.setPotentialRegisters(equivalenceClass, potentials); } } getProgram().setRegisterPotentials(registerPotentials); } + /** + * Determines if a variable reference is ever created for the variable using the address-of "&" operator + * @param equivalenceClass The equivalence class + * @return true if a variable reference is extracted + */ + private boolean varRefExtracted(LiveRangeEquivalenceClass equivalenceClass) { + Collection allConstants = getProgram().getScope().getAllConstants(true); + for(ConstantVar allConstant : allConstants) { + if(allConstant.getValue() instanceof ConstantVarPointer) { + VariableRef toVar = ((ConstantVarPointer) allConstant.getValue()).getToVar(); + if(equivalenceClass.getVariables().contains(toVar)) { + return true; + } + } + } + return false; + } + + private boolean hasIntersection(Collection vars1, List vars2) { + ArrayList set = new ArrayList<>(vars1); + set.retainAll(vars2); + return set.size() > 0; + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java index 31816a725..3e41d959e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java @@ -96,6 +96,8 @@ public class PassNVariableReferenceInfos extends Pass2Base { return used; } else if(rValue instanceof CastValue) { return getReferenced(((CastValue) rValue).getValue()); + } else if(rValue instanceof ConstantVarPointer) { + return getReferenced(((ConstantVarPointer) rValue).getToVar()); } else { throw new RuntimeException("Unhandled RValue type " + rValue); } diff --git a/src/main/java/dk/camelot64/kickc/passes/ValueReplacer.java b/src/main/java/dk/camelot64/kickc/passes/ValueReplacer.java index 1698b6c7e..bbd5efdc1 100644 --- a/src/main/java/dk/camelot64/kickc/passes/ValueReplacer.java +++ b/src/main/java/dk/camelot64/kickc/passes/ValueReplacer.java @@ -105,6 +105,8 @@ public class ValueReplacer { } } else if(value instanceof CastValue) { subValues.add(new ReplaceableCastValue((CastValue) value)); + } else if(value instanceof ConstantVarPointer) { + subValues.add(new ReplaceableVarPointer((ConstantVarPointer) value)); } return subValues; } @@ -178,6 +180,28 @@ public class ValueReplacer { } + /** + * Replaceable pointer inside a variable pointer. + */ + public static class ReplaceableVarPointer extends ReplaceableValue { + private final ConstantVarPointer varPointer; + + + public ReplaceableVarPointer(ConstantVarPointer varPointer) { + this.varPointer = varPointer; + } + + @Override + public RValue get() { + return varPointer.getToVar(); + } + + @Override + public void set(RValue val) { + varPointer.setToVar((VariableRef) val); + } + + } public static class ReplaceableListElement extends ReplaceableValue { private ValueList list; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index df30ca8d1..d745ddc14 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,6 +42,11 @@ public class TestPrograms { AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } + @Test + public void testAddressOf() throws IOException, URISyntaxException { + compileAndCompare("test-address-of"); + } + @Test public void testDivision() throws IOException, URISyntaxException { compileAndCompare("test-division"); diff --git a/src/test/java/dk/camelot64/kickc/test/kc/test-address-of.kc b/src/test/java/dk/camelot64/kickc/test/kc/test-address-of.kc new file mode 100644 index 000000000..0042efc2c --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/kc/test-address-of.kc @@ -0,0 +1,10 @@ + +void main() { + byte* SCREEN = $400; + for( byte b: 0..10) { + byte* bp = &b; + byte c = *bp +1; + SCREEN[b] = c; + } +} + diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.asm b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.asm new file mode 100644 index 000000000..9d0c7a9b1 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.asm @@ -0,0 +1,22 @@ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + jsr main +main: { + .label SCREEN = $400 + .label bp = b + .label b = 2 + lda #0 + sta b + b1: + lda bp + clc + adc #1 + ldy b + sta SCREEN,y + inc b + lda b + cmp #$b + bne b1 + rts +} diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.cfg b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.cfg new file mode 100644 index 000000000..677391575 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.cfg @@ -0,0 +1,22 @@ +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@1 +@1: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main param-assignment [ ] ( ) + to:@end +@end: scope:[] from @1 + [3] phi() [ ] ( ) +main: scope:[main] from @1 + [4] phi() [ ] ( main:2 [ ] ) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::b#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@1/(byte) main::b#1 ) [ main::b#2 ] ( main:2 [ main::b#2 ] ) + [6] (byte) main::c#0 ← *((const byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::b#2 main::c#0 ] ( main:2 [ main::b#2 main::c#0 ] ) + [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) + [8] (byte) main::b#1 ← ++ (byte) main::b#2 [ main::b#1 ] ( main:2 [ main::b#1 ] ) + [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) + to:main::@return +main::@return: scope:[main] from main::@1 + [10] return [ ] ( main:2 [ ] ) + to:@return diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.log b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.log new file mode 100644 index 000000000..8fdfcdb95 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.log @@ -0,0 +1,462 @@ +PARSING src/test/java/dk/camelot64/kickc/test/kc/test-address-of.kc + +void main() { + byte* SCREEN = $400; + for( byte b: 0..10) { + byte* bp = &b; + byte c = *bp +1; + SCREEN[b] = c; + } +} + + + +STATEMENTS +proc (void()) main() + (byte*) main::SCREEN ← (word/signed word/dword/signed dword) 1024 + (byte) main::b ← (byte/signed byte/word/signed word/dword/signed dword) 0 +main::@1: + (byte*~) main::$0 ← & (byte) main::b + (byte*) main::bp ← (byte*~) main::$0 + (byte/word~) main::$1 ← *((byte*) main::bp) + (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) main::c ← (byte/word~) main::$1 + *((byte*) main::SCREEN + (byte) main::b) ← (byte) main::c + (byte) main::b ← ++ (byte) main::b + (boolean~) main::$2 ← (byte) main::b != (byte/signed byte/word/signed word/dword/signed dword) 11 + if((boolean~) main::$2) goto main::@1 +main::@return: + return +endproc // main() + call main + +SYMBOLS +(void()) main() +(byte*~) main::$0 +(byte/word~) main::$1 +(boolean~) main::$2 +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(byte) main::b +(byte*) main::bp +(byte) main::c + +Promoting word/signed word/dword/signed dword to byte* in main::SCREEN ← ((byte*)) 1024 +INITIAL CONTROL FLOW GRAPH +@begin: scope:[] from + to:@1 +main: scope:[main] from + (byte*) main::SCREEN ← ((byte*)) (word/signed word/dword/signed dword) 1024 + (byte) main::b ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte*~) main::$0 ← & (byte) main::b + (byte*) main::bp ← (byte*~) main::$0 + (byte/word~) main::$1 ← *((byte*) main::bp) + (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) main::c ← (byte/word~) main::$1 + *((byte*) main::SCREEN + (byte) main::b) ← (byte) main::c + (byte) main::b ← ++ (byte) main::b + (boolean~) main::$2 ← (byte) main::b != (byte/signed byte/word/signed word/dword/signed dword) 11 + if((boolean~) main::$2) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return +@1: scope:[] from @begin + call main + to:@end +@end: scope:[] from @1 + +Removing empty block main::@2 +PROCEDURE MODIFY VARIABLE ANALYSIS + +Completing Phi functions... + +CONTROL FLOW GRAPH SSA WITH ASSIGNMENT CALL & RETURN +@begin: scope:[] from + to:@1 +main: scope:[main] from @1 + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) 1024 + (byte) main::b#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte*) main::SCREEN#1 ← phi( main/(byte*) main::SCREEN#0 main::@1/(byte*) main::SCREEN#1 ) + (byte) main::b#2 ← phi( main/(byte) main::b#0 main::@1/(byte) main::b#1 ) + (byte*~) main::$0 ← & (byte) main::b#2 + (byte*) main::bp#0 ← (byte*~) main::$0 + (byte/word~) main::$1 ← *((byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) main::c#0 ← (byte/word~) main::$1 + *((byte*) main::SCREEN#1 + (byte) main::b#2) ← (byte) main::c#0 + (byte) main::b#1 ← ++ (byte) main::b#2 + (boolean~) main::$2 ← (byte) main::b#1 != (byte/signed byte/word/signed word/dword/signed dword) 11 + if((boolean~) main::$2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + call main param-assignment + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) main() +(byte*~) main::$0 +(byte/word~) main::$1 +(boolean~) main::$2 +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(byte*) main::SCREEN#1 +(byte) main::b +(byte) main::b#0 +(byte) main::b#1 +(byte) main::b#2 +(byte*) main::bp +(byte*) main::bp#0 +(byte) main::c +(byte) main::c#0 + +OPTIMIZING CONTROL FLOW GRAPH +Culled Empty Block (label) @2 +Succesful SSA optimization Pass2CullEmptyBlocks +Alias (byte*) main::bp#0 = (byte*~) main::$0 +Alias (byte) main::c#0 = (byte/word~) main::$1 +Succesful SSA optimization Pass2AliasElimination +Self Phi Eliminated (byte*) main::SCREEN#1 +Succesful SSA optimization Pass2SelfPhiElimination +Redundant Phi (byte*) main::SCREEN#1 (byte*) main::SCREEN#0 +Succesful SSA optimization Pass2RedundantPhiElimination +Simple Condition (boolean~) main::$2 if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 +Succesful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::SCREEN#0 = ((byte*))1024 +Constant (const byte) main::b#0 = 0 +Constant (const byte*) main::bp#0 = dk.camelot64.kickc.model.ConstantVarPointer@655a5d9c +Succesful SSA optimization Pass2ConstantIdentification +OPTIMIZING CONTROL FLOW GRAPH +Inlining constant with var siblings (const byte) main::b#0 +Inlining constant with var siblings (const byte) main::b#0 +Constant inlined main::b#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Succesful SSA optimization Pass2ConstantInlining +Block Sequence Planned @begin @1 @end main main::@1 main::@return +Added new block during phi lifting main::@3(between main::@1 and main::@1) +Block Sequence Planned @begin @1 @end main main::@1 main::@return main::@3 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 + +Propagating live ranges... +Propagating live ranges... +Propagating live ranges... +Created 1 initial phi equivalence classes +Coalesced [11] main::b#3 ← main::b#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@3 +Block Sequence Planned @begin @1 @end main main::@1 main::@return +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Propagating live ranges... +Propagating live ranges... +Propagating live ranges... + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@1 +@1: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main param-assignment [ ] ( ) + to:@end +@end: scope:[] from @1 + [3] phi() [ ] ( ) +main: scope:[main] from @1 + [4] phi() [ ] ( main:2 [ ] ) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::b#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@1/(byte) main::b#1 ) [ main::b#2 ] ( main:2 [ main::b#2 ] ) + [6] (byte) main::c#0 ← *((const byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::b#2 main::c#0 ] ( main:2 [ main::b#2 main::c#0 ] ) + [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) + [8] (byte) main::b#1 ← ++ (byte) main::b#2 [ main::b#1 ] ( main:2 [ main::b#1 ] ) + [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) + to:main::@return +main::@return: scope:[main] from main::@1 + [10] return [ ] ( main:2 [ ] ) + to:@return + +DOMINATORS +@begin dominated by @begin +@1 dominated by @1 @begin +@end dominated by @1 @begin @end +main dominated by @1 @begin main +main::@1 dominated by @1 @begin main::@1 main +main::@return dominated by main::@return @1 @begin main::@1 main + +NATURAL LOOPS +Found back edge: Loop head: main::@1 tails: main::@1 blocks: null +Populated: Loop head: main::@1 tails: main::@1 blocks: main::@1 +Loop head: main::@1 tails: main::@1 blocks: main::@1 + +NATURAL LOOPS WITH DEPTH +Found 0 loops in scope [] +Found 1 loops in scope [main] + Loop head: main::@1 tails: main::@1 blocks: main::@1 +Loop head: main::@1 tails: main::@1 blocks: main::@1 depth: 1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte*) main::SCREEN +(byte) main::b +(byte) main::b#1 16.5 +(byte) main::b#2 11.0 +(byte*) main::bp +(byte) main::c +(byte) main::c#0 22.0 + +Initial phi equivalence classes +[ main::b#2 main::b#1 ] +Added variable main::c#0 to zero page equivalence class [ main::c#0 ] +Complete equivalence classes +[ main::b#2 main::b#1 ] +[ main::c#0 ] +Allocated zp ZP_BYTE:2 [ main::b#2 main::b#1 ] +Allocated zp ZP_BYTE:3 [ main::c#0 ] + +INITIAL ASM +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + .label bp = b + .label c = 3 + .label b = 2 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG11 [5] phi (byte) main::b#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta b + jmp b1 + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG13 [5] phi (byte) main::b#2 = (byte) main::b#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG14 main::@1 + b1: + //SEG15 [6] (byte) main::c#0 ← *((const byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::b#2 main::c#0 ] ( main:2 [ main::b#2 main::c#0 ] ) -- vbuz1=_deref_pbuc1_plus_1 + ldy bp + iny + sty c + //SEG16 [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) -- pbuc1_derefidx_vbuz1=vbuz2 + lda c + ldy b + sta SCREEN,y + //SEG17 [8] (byte) main::b#1 ← ++ (byte) main::b#2 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1=_inc_vbuz1 + inc b + //SEG18 [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1_neq_vbuc1_then_la1 + lda b + cmp #$b + bne b1_from_b1 + jmp breturn + //SEG19 main::@return + breturn: + //SEG20 [10] return [ ] ( main:2 [ ] ) + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) always clobbers reg byte y +Statement [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::b#2 main::b#1 ] : zp ZP_BYTE:2 , +Potential registers zp ZP_BYTE:3 [ main::c#0 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 27.5: zp ZP_BYTE:2 [ main::b#2 main::b#1 ] 22: zp ZP_BYTE:3 [ main::c#0 ] +Uplift Scope [] + +Uplifting [main] best 443 combination zp ZP_BYTE:2 [ main::b#2 main::b#1 ] reg byte a [ main::c#0 ] +Uplifting [] best 443 combination +Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::b#2 main::b#1 ] +Uplifting [main] best 443 combination zp ZP_BYTE:2 [ main::b#2 main::b#1 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + .label bp = b + .label b = 2 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG11 [5] phi (byte) main::b#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta b + jmp b1 + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG13 [5] phi (byte) main::b#2 = (byte) main::b#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG14 main::@1 + b1: + //SEG15 [6] (byte) main::c#0 ← *((const byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::b#2 main::c#0 ] ( main:2 [ main::b#2 main::c#0 ] ) -- vbuaa=_deref_pbuc1_plus_1 + lda bp + clc + adc #1 + //SEG16 [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) -- pbuc1_derefidx_vbuz1=vbuaa + ldy b + sta SCREEN,y + //SEG17 [8] (byte) main::b#1 ← ++ (byte) main::b#2 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1=_inc_vbuz1 + inc b + //SEG18 [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1_neq_vbuc1_then_la1 + lda b + cmp #$b + bne b1_from_b1 + jmp breturn + //SEG19 main::@return + breturn: + //SEG20 [10] return [ ] ( main:2 [ ] ) + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b1 with b1 +Removing instruction bbegin: +Removing instruction b1_from_bbegin: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b1: +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) 1024 +(byte) main::b +(byte) main::b#1 b zp ZP_BYTE:2 16.5 +(byte) main::b#2 b zp ZP_BYTE:2 11.0 +(byte*) main::bp +(const byte*) main::bp#0 bp = &(byte) main::b#2 +(byte) main::c +(byte) main::c#0 reg byte a 22.0 + +zp ZP_BYTE:2 [ main::b#2 main::b#1 ] +reg byte a [ main::c#0 ] + + +FINAL ASSEMBLER +Score: 347 + +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG4 @1 +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .label SCREEN = $400 + .label bp = b + .label b = 2 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG11 [5] phi (byte) main::b#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta b + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + //SEG13 [5] phi (byte) main::b#2 = (byte) main::b#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG14 main::@1 + b1: + //SEG15 [6] (byte) main::c#0 ← *((const byte*) main::bp#0) + (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::b#2 main::c#0 ] ( main:2 [ main::b#2 main::c#0 ] ) -- vbuaa=_deref_pbuc1_plus_1 + lda bp + clc + adc #1 + //SEG16 [7] *((const byte*) main::SCREEN#0 + (byte) main::b#2) ← (byte) main::c#0 [ main::b#2 ] ( main:2 [ main::b#2 ] ) -- pbuc1_derefidx_vbuz1=vbuaa + ldy b + sta SCREEN,y + //SEG17 [8] (byte) main::b#1 ← ++ (byte) main::b#2 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1=_inc_vbuz1 + inc b + //SEG18 [9] if((byte) main::b#1!=(byte/signed byte/word/signed word/dword/signed dword) 11) goto main::@1 [ main::b#1 ] ( main:2 [ main::b#1 ] ) -- vbuz1_neq_vbuc1_then_la1 + lda b + cmp #$b + bne b1 + //SEG19 main::@return + //SEG20 [10] return [ ] ( main:2 [ ] ) + rts +} + diff --git a/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.sym b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.sym new file mode 100644 index 000000000..0f8b3ded1 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/test-address-of.sym @@ -0,0 +1,18 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) 1024 +(byte) main::b +(byte) main::b#1 b zp ZP_BYTE:2 16.5 +(byte) main::b#2 b zp ZP_BYTE:2 11.0 +(byte*) main::bp +(const byte*) main::bp#0 bp = &(byte) main::b#2 +(byte) main::c +(byte) main::c#0 reg byte a 22.0 + +zp ZP_BYTE:2 [ main::b#2 main::b#1 ] +reg byte a [ main::c#0 ]