From 7fb7d3acf67e2fd0f4bfba94fbc4ddf43c3f4e09 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sat, 7 Sep 2019 18:23:11 +0200 Subject: [PATCH] Optimized live ranges by statement idx in PassNLiveRangeVariables. Added example of how to use segments to compile code meant for transfer to zeropage. --- .../dk/camelot64/kickc/model/LiveRange.java | 8 + .../kickc/model/LiveRangeVariables.java | 47 + .../calcs/PassNCalcLiveRangeVariables.java | 3 +- .../dk/camelot64/kickc/test/TestPrograms.java | 7 + src/test/kc/examples/zpcode/zpcode.kc | 46 + src/test/kc/examples/zpcode/zpcode.ld | 9 + src/test/ref/examples/zpcode/zpcode.asm | 64 ++ src/test/ref/examples/zpcode/zpcode.cfg | 58 ++ src/test/ref/examples/zpcode/zpcode.log | 922 ++++++++++++++++++ src/test/ref/examples/zpcode/zpcode.sym | 38 + 10 files changed, 1201 insertions(+), 1 deletion(-) create mode 100644 src/test/kc/examples/zpcode/zpcode.kc create mode 100644 src/test/kc/examples/zpcode/zpcode.ld create mode 100644 src/test/ref/examples/zpcode/zpcode.asm create mode 100644 src/test/ref/examples/zpcode/zpcode.cfg create mode 100644 src/test/ref/examples/zpcode/zpcode.log create mode 100644 src/test/ref/examples/zpcode/zpcode.sym diff --git a/src/main/java/dk/camelot64/kickc/model/LiveRange.java b/src/main/java/dk/camelot64/kickc/model/LiveRange.java index 708b2294f..fa89cb4ac 100644 --- a/src/main/java/dk/camelot64/kickc/model/LiveRange.java +++ b/src/main/java/dk/camelot64/kickc/model/LiveRange.java @@ -33,6 +33,14 @@ public class LiveRange { return s; } + /** + * Get the underlying statement intervals + * @return The intervals + */ + public List getIntervals() { + return intervals; + } + /** * Add an index to the live range * diff --git a/src/main/java/dk/camelot64/kickc/model/LiveRangeVariables.java b/src/main/java/dk/camelot64/kickc/model/LiveRangeVariables.java index 8e0f0134d..834a0ed74 100644 --- a/src/main/java/dk/camelot64/kickc/model/LiveRangeVariables.java +++ b/src/main/java/dk/camelot64/kickc/model/LiveRangeVariables.java @@ -78,8 +78,55 @@ public class LiveRangeVariables { return liveRanges.get(variable); } + /** + * Get the number of variable live ranges + * @return The number of variable live ranges + */ public int size() { return liveRanges.size(); + } + + /** + * Get variable live ranges by statement index. Usable for quick access if you plan to iterate many statements. + * @return variable live ranges by statement index + */ + public LiveRangeVariablesByStatement getLiveRangeVariablesByStatement() { + LiveRangeVariablesByStatement liveRangeVariablesByStatement = new LiveRangeVariablesByStatement(); + for(Map.Entry variableRefLiveRangeEntry : liveRanges.entrySet()) { + VariableRef variableRef = variableRefLiveRangeEntry.getKey(); + LiveRange liveRange = variableRefLiveRangeEntry.getValue(); + for(LiveRange.LiveInterval liveInterval : liveRange.getIntervals()) { + for(int stmtIdx = liveInterval.getFirstStatementIdx(); stmtIdx<= liveInterval.getLastStatementIdx(); stmtIdx++) { + liveRangeVariablesByStatement.addAlive(stmtIdx, variableRef); + } + } + } + return liveRangeVariablesByStatement; + } + + /** Variable live ranges by statement index. Usable for quick access if you plan to iterate many statements. */ + public static class LiveRangeVariablesByStatement { + + private ArrayList> aliveByStatementIdx; + + public LiveRangeVariablesByStatement() { + this.aliveByStatementIdx = new ArrayList<>(); + } + + public void addAlive(int statementIdx, VariableRef variableRef) { + while(statementIdx>=aliveByStatementIdx.size()) { + aliveByStatementIdx.add(new ArrayList<>()); + } + List variableRefs = aliveByStatementIdx.get(statementIdx); + variableRefs.add(variableRef); + } + + + public List getAlive(int statementIdx) { + if(statementIdx>=aliveByStatementIdx.size()) + return new ArrayList<>(); + return aliveByStatementIdx.get(statementIdx); + } } diff --git a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangeVariables.java b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangeVariables.java index 901fc228b..f517b8d7b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangeVariables.java +++ b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangeVariables.java @@ -80,9 +80,10 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase aliveNextStmt = liveRanges.getAlive(stmt.getIndex()); + List aliveNextStmt = liveRangeVariablesByStatement.getAlive(stmt.getIndex()); Collection definedNextStmt = referenceInfo.getDefinedVars(stmt); initLiveRange(liveRanges, definedNextStmt); Collection previousStmts = getPreviousStatements(stmt); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 29332c88b..b63cbd180 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -36,6 +36,13 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testZpCode() throws IOException, URISyntaxException { + compileAndCompare("examples/zpcode/zpcode"); + } + + + // Fix parameter type problem - https://gitlab.com/camelot/kickc/issues/299 /* @Test diff --git a/src/test/kc/examples/zpcode/zpcode.kc b/src/test/kc/examples/zpcode/zpcode.kc new file mode 100644 index 000000000..4f707441b --- /dev/null +++ b/src/test/kc/examples/zpcode/zpcode.kc @@ -0,0 +1,46 @@ +// Example showing how to use KickAsm segments to compile meant to be transfered to zeropage before execution. +// The linker-file defines the ZpCode segment to be on zeropage and does not include it directly in the PRG-file (by excluding it from the Program segment). +// Instead the compiled code is added as an array of bytes in "normal" memory - and transferred to zeropage at the start of the program + +#pragma link("zpcode.ld") + +char* RASTER = 0xd012; +char* BGCOL = 0xd020; + +void main() { + asm { sei } + // Transfer ZP-code to zeropage + char* zpCode = (char*)&zpLoop; + for(char i=0;i<20;i++) + zpCode[i] = zpCodeData[i]; + + while(true) { + while(*RASTER!=0xff) {} + // Call code in normal memory + loop(); + // Call code on zeropage + zpLoop(); + *BGCOL = 0; + } +} + +// Code in "normal" memory +void loop() { + for(char i:0..100) { + (*BGCOL)--; + } +} + +// Array containing the zeropage code to be transferred to zeropage before execution +char[] zpCodeData = kickasm {{ + .segmentout [segments="ZpCode"] +}}; + +// Code that will be placed on zeropage +// It won't be output to the PRG-file directly because it is not included in the Program segment in the linker-file. +#pragma code_seg(ZpCode) +void zpLoop() { + for(char i:0..100) { + (*BGCOL)++; + } +} diff --git a/src/test/kc/examples/zpcode/zpcode.ld b/src/test/kc/examples/zpcode/zpcode.ld new file mode 100644 index 000000000..293a31861 --- /dev/null +++ b/src/test/kc/examples/zpcode/zpcode.ld @@ -0,0 +1,9 @@ +.file [name="%O.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$0810] +.segmentdef Data [startAfter="Code"] +.segmentdef ZpCode [start=$80] +.segment Basic +:BasicUpstart(main) + diff --git a/src/test/ref/examples/zpcode/zpcode.asm b/src/test/ref/examples/zpcode/zpcode.asm new file mode 100644 index 000000000..56661274c --- /dev/null +++ b/src/test/ref/examples/zpcode/zpcode.asm @@ -0,0 +1,64 @@ +// Example showing how to use KickAsm segments to compile meant to be transfered to zeropage before execution. +// The linker-file defines the ZpCode segment to be on zeropage and does not include it directly in the PRG-file (by excluding it from the Program segment). +// Instead the compiled code is added as an array of bytes in "normal" memory - and transferred to zeropage at the start of the program + .file [name="zpcode.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$0810] +.segmentdef Data [startAfter="Code"] +.segmentdef ZpCode [start=$80] +.segment Basic +:BasicUpstart(main) + + + .label RASTER = $d012 + .label BGCOL = $d020 +.segment Code +main: { + .label zpCode = zpLoop + sei + ldx #0 + b1: + cpx #$14 + bcc b2 + b3: + lda #$ff + cmp RASTER + bne b3 + jsr loop + jsr zpLoop + lda #0 + sta BGCOL + jmp b3 + b2: + lda zpCodeData,x + sta zpCode,x + inx + jmp b1 +} +.segment ZpCode +zpLoop: { + ldx #0 + b1: + inc BGCOL + inx + cpx #$65 + bne b1 + rts +} +.segment Code +// Code in "normal" memory +loop: { + ldx #0 + b1: + dec BGCOL + inx + cpx #$65 + bne b1 + rts +} +.segment Data +// Array containing the zeropage code to be transferred to zeropage before execution +zpCodeData: +.segmentout [segments="ZpCode"] + diff --git a/src/test/ref/examples/zpcode/zpcode.cfg b/src/test/ref/examples/zpcode/zpcode.cfg new file mode 100644 index 000000000..a1de52c29 --- /dev/null +++ b/src/test/ref/examples/zpcode/zpcode.cfg @@ -0,0 +1,58 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + asm { sei } + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) main::i#2<(byte) $14) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 main::@3 main::@6 + [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@3 + [8] phi() + [9] call loop + to:main::@5 +main::@5: scope:[main] from main::@4 + [10] phi() + [11] call zpLoop + to:main::@6 +main::@6: scope:[main] from main::@5 + [12] *((const byte*) BGCOL#0) ← (byte) 0 + to:main::@3 +main::@2: scope:[main] from main::@1 + [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) + [14] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 +zpLoop: scope:[zpLoop] from main::@5 + [15] phi() + to:zpLoop::@1 +zpLoop::@1: scope:[zpLoop] from zpLoop zpLoop::@1 + [16] (byte) zpLoop::i#2 ← phi( zpLoop/(byte) 0 zpLoop::@1/(byte) zpLoop::i#1 ) + [17] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) + [18] (byte) zpLoop::i#1 ← ++ (byte) zpLoop::i#2 + [19] if((byte) zpLoop::i#1!=(byte) $65) goto zpLoop::@1 + to:zpLoop::@return +zpLoop::@return: scope:[zpLoop] from zpLoop::@1 + [20] return + to:@return +loop: scope:[loop] from main::@4 + [21] phi() + to:loop::@1 +loop::@1: scope:[loop] from loop loop::@1 + [22] (byte) loop::i#2 ← phi( loop/(byte) 0 loop::@1/(byte) loop::i#1 ) + [23] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0) + [24] (byte) loop::i#1 ← ++ (byte) loop::i#2 + [25] if((byte) loop::i#1!=(byte) $65) goto loop::@1 + to:loop::@return +loop::@return: scope:[loop] from loop::@1 + [26] return + to:@return diff --git a/src/test/ref/examples/zpcode/zpcode.log b/src/test/ref/examples/zpcode/zpcode.log new file mode 100644 index 000000000..ad568f782 --- /dev/null +++ b/src/test/ref/examples/zpcode/zpcode.log @@ -0,0 +1,922 @@ +Loading link script "zpcode.ld" +Resolved forward reference zpLoop to (void()) zpLoop() +Resolved forward reference zpCodeData to (byte[]) zpCodeData +Identified constant variable (byte*) RASTER +Identified constant variable (byte*) BGCOL +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) main::@8 +Culled Empty Block (label) main::@17 +Culled Empty Block (label) main::@9 +Culled Empty Block (label) main::@18 +Culled Empty Block (label) main::@11 +Culled Empty Block (label) main::@13 +Culled Empty Block (label) main::@14 +Culled Empty Block (label) main::@15 +Culled Empty Block (label) main::@16 +Culled Empty Block (label) @1 +Culled Empty Block (label) loop::@2 +Culled Empty Block (label) zpLoop::@2 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) RASTER#0 ← ((byte*)) (number) $d012 + (byte*) BGCOL#0 ← ((byte*)) (number) $d020 + to:@2 +main: scope:[main] from @3 + asm { sei } + (void()*~) main::$0 ← & (void()) zpLoop() + (byte*~) main::$1 ← ((byte*)) (void()*~) main::$0 + (byte*) main::zpCode#0 ← (byte*~) main::$1 + (byte) main::i#0 ← (number) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte*) main::zpCode#2 ← phi( main/(byte*) main::zpCode#0 main::@2/(byte*) main::zpCode#1 ) + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + (bool~) main::$2 ← (byte) main::i#2 < (number) $14 + if((bool~) main::$2) goto main::@2 + to:main::@7 +main::@2: scope:[main] from main::@1 + (byte*) main::zpCode#1 ← phi( main::@1/(byte*) main::zpCode#2 ) + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + *((byte*) main::zpCode#1 + (byte) main::i#3) ← *((byte[]) zpCodeData#0 + (byte) main::i#3) + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@7: scope:[main] from main::@1 main::@20 + if(true) goto main::@10 + to:main::@return +main::@10: scope:[main] from main::@10 main::@7 + (bool~) main::$3 ← *((byte*) RASTER#0) != (number) $ff + if((bool~) main::$3) goto main::@10 + to:main::@12 +main::@12: scope:[main] from main::@10 + call loop + to:main::@19 +main::@19: scope:[main] from main::@12 + call zpLoop + to:main::@20 +main::@20: scope:[main] from main::@19 + *((byte*) BGCOL#0) ← (number) 0 + to:main::@7 +main::@return: scope:[main] from main::@7 + return + to:@return +loop: scope:[loop] from main::@12 + (byte) loop::i#0 ← (byte) 0 + to:loop::@1 +loop::@1: scope:[loop] from loop loop::@1 + (byte) loop::i#2 ← phi( loop/(byte) loop::i#0 loop::@1/(byte) loop::i#1 ) + *((byte*) BGCOL#0) ← -- *((byte*) BGCOL#0) + (byte) loop::i#1 ← (byte) loop::i#2 + rangenext(0,$64) + (bool~) loop::$1 ← (byte) loop::i#1 != rangelast(0,$64) + if((bool~) loop::$1) goto loop::@1 + to:loop::@return +loop::@return: scope:[loop] from loop::@1 + return + to:@return +@2: scope:[] from @begin + (byte[]) zpCodeData#0 ← kickasm {{ .segmentout [segments="ZpCode"] + }} + to:@3 +zpLoop: scope:[zpLoop] from main::@19 + (byte) zpLoop::i#0 ← (byte) 0 + to:zpLoop::@1 +zpLoop::@1: scope:[zpLoop] from zpLoop zpLoop::@1 + (byte) zpLoop::i#2 ← phi( zpLoop/(byte) zpLoop::i#0 zpLoop::@1/(byte) zpLoop::i#1 ) + *((byte*) BGCOL#0) ← ++ *((byte*) BGCOL#0) + (byte) zpLoop::i#1 ← (byte) zpLoop::i#2 + rangenext(0,$64) + (bool~) zpLoop::$1 ← (byte) zpLoop::i#1 != rangelast(0,$64) + if((bool~) zpLoop::$1) goto zpLoop::@1 + to:zpLoop::@return +zpLoop::@return: scope:[zpLoop] from zpLoop::@1 + return + to:@return +@3: scope:[] from @2 + call main + to:@4 +@4: scope:[] from @3 + to:@end +@end: scope:[] from @4 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @4 +(label) @begin +(label) @end +(byte*) BGCOL +(byte*) BGCOL#0 +(byte*) RASTER +(byte*) RASTER#0 +(void()) loop() +(bool~) loop::$1 +(label) loop::@1 +(label) loop::@return +(byte) loop::i +(byte) loop::i#0 +(byte) loop::i#1 +(byte) loop::i#2 +(void()) main() +(void()*~) main::$0 +(byte*~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@10 +(label) main::@12 +(label) main::@19 +(label) main::@2 +(label) main::@20 +(label) main::@7 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte*) main::zpCode +(byte*) main::zpCode#0 +(byte*) main::zpCode#1 +(byte*) main::zpCode#2 +(byte[]) zpCodeData +(byte[]) zpCodeData#0 +(void()) zpLoop() +(bool~) zpLoop::$1 +(label) zpLoop::@1 +(label) zpLoop::@return +(byte) zpLoop::i +(byte) zpLoop::i#0 +(byte) zpLoop::i#1 +(byte) zpLoop::i#2 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) $14 in (bool~) main::$2 ← (byte) main::i#2 < (number) $14 +Adding number conversion cast (unumber) $ff in (bool~) main::$3 ← *((byte*) RASTER#0) != (number) $ff +Adding number conversion cast (unumber) 0 in *((byte*) BGCOL#0) ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) RASTER#0 ← (byte*)(number) $d012 +Inlining cast (byte*) BGCOL#0 ← (byte*)(number) $d020 +Inlining cast (byte*~) main::$1 ← (byte*)(void()*~) main::$0 +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Inlining cast *((byte*) BGCOL#0) ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 53266 +Simplifying constant pointer cast (byte*) 53280 +Simplifying constant integer cast 0 +Simplifying constant integer cast $14 +Simplifying constant integer cast $ff +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $14 +Finalized unsigned number type (byte) $ff +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte*) main::zpCode#0 = (byte*~) main::$1 +Alias (byte) main::i#2 = (byte) main::i#3 +Alias (byte*) main::zpCode#1 = (byte*) main::zpCode#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte*) main::zpCode#1 (byte*) main::zpCode#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) main::$2 [9] if((byte) main::i#2<(byte) $14) goto main::@2 +Simple Condition (bool~) main::$3 [15] if(*((byte*) RASTER#0)!=(byte) $ff) goto main::@10 +Simple Condition (bool~) loop::$1 [25] if((byte) loop::i#1!=rangelast(0,$64)) goto loop::@1 +Simple Condition (bool~) zpLoop::$1 [33] if((byte) zpLoop::i#1!=rangelast(0,$64)) goto zpLoop::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [3] (void()*~) main::$0 ← & (void()) zpLoop() +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) RASTER#0 = (byte*) 53266 +Constant (const byte*) BGCOL#0 = (byte*) 53280 +Constant (const void()*) main::$0 = &zpLoop +Constant (const byte) main::i#0 = 0 +Constant (const byte) loop::i#0 = 0 +Constant (const byte[]) zpCodeData#0 = kickasm {{ .segmentout [segments="ZpCode"] + }} +Constant (const byte) zpLoop::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)main::$0 in [4] (byte*) main::zpCode#0 ← (byte*)(const void()*) main::$0 +Successful SSA optimization Pass2ConstantValues +if() condition always true - replacing block destination [13] if(true) goto main::@10 +Successful SSA optimization Pass2ConstantIfs +Resolved ranged next value [23] loop::i#1 ← ++ loop::i#2 to ++ +Resolved ranged comparison value [25] if(loop::i#1!=rangelast(0,$64)) goto loop::@1 to (number) $65 +Resolved ranged next value [31] zpLoop::i#1 ← ++ zpLoop::i#2 to ++ +Resolved ranged comparison value [33] if(zpLoop::i#1!=rangelast(0,$64)) goto zpLoop::@1 to (number) $65 +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Adding number conversion cast (unumber) $65 in if((byte) loop::i#1!=(number) $65) goto loop::@1 +Adding number conversion cast (unumber) $65 in if((byte) zpLoop::i#1!=(number) $65) goto zpLoop::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast $65 +Simplifying constant integer cast $65 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $65 +Finalized unsigned number type (byte) $65 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant (const byte*) main::zpCode#0 = (byte*)main::$0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) loop::i#0 +Inlining constant with var siblings (const byte) zpLoop::i#0 +Constant inlined main::i#0 = (byte) 0 +Constant inlined loop::i#0 = (byte) 0 +Constant inlined zpLoop::i#0 = (byte) 0 +Constant inlined main::$0 = &(void()) zpLoop() +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting loop::@3(between loop::@1 and loop::@1) +Added new block during phi lifting zpLoop::@3(between zpLoop::@1 and zpLoop::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @4 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main::@7 +Adding NOP phi() at start of main::@12 +Adding NOP phi() at start of main::@19 +Adding NOP phi() at start of zpLoop +Adding NOP phi() at start of loop +CALL GRAPH +Calls in [] to main:3 +Calls in [main] to loop:12 zpLoop:14 + +Created 3 initial phi equivalence classes +Coalesced [18] main::i#4 ← main::i#1 +Coalesced [25] zpLoop::i#3 ← zpLoop::i#1 +Coalesced [32] loop::i#3 ← loop::i#1 +Coalesced down to 3 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) @4 +Culled Empty Block (label) main::@7 +Culled Empty Block (label) zpLoop::@3 +Culled Empty Block (label) loop::@3 +Renumbering block @3 to @1 +Renumbering block main::@10 to main::@3 +Renumbering block main::@12 to main::@4 +Renumbering block main::@19 to main::@5 +Renumbering block main::@20 to main::@6 +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::@4 +Adding NOP phi() at start of main::@5 +Adding NOP phi() at start of zpLoop +Adding NOP phi() at start of loop + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + asm { sei } + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) main::i#2<(byte) $14) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 main::@3 main::@6 + [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@3 + [8] phi() + [9] call loop + to:main::@5 +main::@5: scope:[main] from main::@4 + [10] phi() + [11] call zpLoop + to:main::@6 +main::@6: scope:[main] from main::@5 + [12] *((const byte*) BGCOL#0) ← (byte) 0 + to:main::@3 +main::@2: scope:[main] from main::@1 + [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) + [14] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 +zpLoop: scope:[zpLoop] from main::@5 + [15] phi() + to:zpLoop::@1 +zpLoop::@1: scope:[zpLoop] from zpLoop zpLoop::@1 + [16] (byte) zpLoop::i#2 ← phi( zpLoop/(byte) 0 zpLoop::@1/(byte) zpLoop::i#1 ) + [17] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) + [18] (byte) zpLoop::i#1 ← ++ (byte) zpLoop::i#2 + [19] if((byte) zpLoop::i#1!=(byte) $65) goto zpLoop::@1 + to:zpLoop::@return +zpLoop::@return: scope:[zpLoop] from zpLoop::@1 + [20] return + to:@return +loop: scope:[loop] from main::@4 + [21] phi() + to:loop::@1 +loop::@1: scope:[loop] from loop loop::@1 + [22] (byte) loop::i#2 ← phi( loop/(byte) 0 loop::@1/(byte) loop::i#1 ) + [23] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0) + [24] (byte) loop::i#1 ← ++ (byte) loop::i#2 + [25] if((byte) loop::i#1!=(byte) $65) goto loop::@1 + to:loop::@return +loop::@return: scope:[loop] from loop::@1 + [26] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) BGCOL +(byte*) RASTER +(void()) loop() +(byte) loop::i +(byte) loop::i#1 151.5 +(byte) loop::i#2 101.0 +(void()) main() +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 18.333333333333332 +(byte*) main::zpCode +(byte[]) zpCodeData +(void()) zpLoop() +(byte) zpLoop::i +(byte) zpLoop::i#1 151.5 +(byte) zpLoop::i#2 101.0 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ zpLoop::i#2 zpLoop::i#1 ] +[ loop::i#2 loop::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ zpLoop::i#2 zpLoop::i#1 ] +[ loop::i#2 loop::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ zpLoop::i#2 zpLoop::i#1 ] +Allocated zp ZP_BYTE:4 [ loop::i#2 loop::i#1 ] + +INITIAL ASM +Target platform is custom + // File Comments +// Example showing how to use KickAsm segments to compile meant to be transfered to zeropage before execution. +// The linker-file defines the ZpCode segment to be on zeropage and does not include it directly in the PRG-file (by excluding it from the Program segment). +// Instead the compiled code is added as an array of bytes in "normal" memory - and transferred to zeropage at the start of the program + // Upstart + .file [name="zpcode.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$0810] +.segmentdef Data [startAfter="Code"] +.segmentdef ZpCode [start=$80] +.segment Basic +:BasicUpstart(main) + + + // Global Constants & labels + .label RASTER = $d012 + .label BGCOL = $d020 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: +.segment Code + // main +main: { + .label zpCode = zpLoop + .label i = 2 + // asm { sei } + sei + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // main::@1 + b1: + // [6] if((byte) main::i#2<(byte) $14) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$14 + bcc b2 + jmp b3 + // main::@3 + b3: + // [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 -- _deref_pbuc1_neq_vbuc2_then_la1 + lda #$ff + cmp RASTER + bne b3 + // [8] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + b4_from_b3: + jmp b4 + // main::@4 + b4: + // [9] call loop + // [21] phi from main::@4 to loop [phi:main::@4->loop] + loop_from_b4: + jsr loop + // [10] phi from main::@4 to main::@5 [phi:main::@4->main::@5] + b5_from_b4: + jmp b5 + // main::@5 + b5: + // [11] call zpLoop + // [15] phi from main::@5 to zpLoop [phi:main::@5->zpLoop] + zpLoop_from_b5: + jsr zpLoop + jmp b6 + // main::@6 + b6: + // [12] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta BGCOL + jmp b3 + // main::@2 + b2: + // [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy.z i + lda zpCodeData,y + sta zpCode,y + // [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} +.segment ZpCode + // zpLoop +zpLoop: { + .label i = 3 + // [16] phi from zpLoop to zpLoop::@1 [phi:zpLoop->zpLoop::@1] + b1_from_zpLoop: + // [16] phi (byte) zpLoop::i#2 = (byte) 0 [phi:zpLoop->zpLoop::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // [16] phi from zpLoop::@1 to zpLoop::@1 [phi:zpLoop::@1->zpLoop::@1] + b1_from_b1: + // [16] phi (byte) zpLoop::i#2 = (byte) zpLoop::i#1 [phi:zpLoop::@1->zpLoop::@1#0] -- register_copy + jmp b1 + // zpLoop::@1 + b1: + // [17] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + // [18] (byte) zpLoop::i#1 ← ++ (byte) zpLoop::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [19] if((byte) zpLoop::i#1!=(byte) $65) goto zpLoop::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$65 + cmp.z i + bne b1_from_b1 + jmp breturn + // zpLoop::@return + breturn: + // [20] return + rts +} +.segment Code + // loop +// Code in "normal" memory +loop: { + .label i = 4 + // [22] phi from loop to loop::@1 [phi:loop->loop::@1] + b1_from_loop: + // [22] phi (byte) loop::i#2 = (byte) 0 [phi:loop->loop::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // [22] phi from loop::@1 to loop::@1 [phi:loop::@1->loop::@1] + b1_from_b1: + // [22] phi (byte) loop::i#2 = (byte) loop::i#1 [phi:loop::@1->loop::@1#0] -- register_copy + jmp b1 + // loop::@1 + b1: + // [23] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BGCOL + // [24] (byte) loop::i#1 ← ++ (byte) loop::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [25] if((byte) loop::i#1!=(byte) $65) goto loop::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$65 + cmp.z i + bne b1_from_b1 + jmp breturn + // loop::@return + breturn: + // [26] return + rts +} + // File Data +.segment Data +// Array containing the zeropage code to be transferred to zeropage before execution +zpCodeData: +.segmentout [segments="ZpCode"] + + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [12] *((const byte*) BGCOL#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [12] *((const byte*) BGCOL#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ zpLoop::i#2 zpLoop::i#1 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ loop::i#2 loop::i#1 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [loop] 252.5: zp ZP_BYTE:4 [ loop::i#2 loop::i#1 ] +Uplift Scope [zpLoop] 252.5: zp ZP_BYTE:3 [ zpLoop::i#2 zpLoop::i#1 ] +Uplift Scope [main] 40.33: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [loop] best 7031 combination reg byte x [ loop::i#2 loop::i#1 ] +Uplifting [zpLoop] best 6131 combination reg byte x [ zpLoop::i#2 zpLoop::i#1 ] +Uplifting [main] best 6011 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 6011 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Example showing how to use KickAsm segments to compile meant to be transfered to zeropage before execution. +// The linker-file defines the ZpCode segment to be on zeropage and does not include it directly in the PRG-file (by excluding it from the Program segment). +// Instead the compiled code is added as an array of bytes in "normal" memory - and transferred to zeropage at the start of the program + // Upstart + .file [name="zpcode.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$0810] +.segmentdef Data [startAfter="Code"] +.segmentdef ZpCode [start=$80] +.segment Basic +:BasicUpstart(main) + + + // Global Constants & labels + .label RASTER = $d012 + .label BGCOL = $d020 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: +.segment Code + // main +main: { + .label zpCode = zpLoop + // asm { sei } + sei + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // main::@1 + b1: + // [6] if((byte) main::i#2<(byte) $14) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$14 + bcc b2 + jmp b3 + // main::@3 + b3: + // [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 -- _deref_pbuc1_neq_vbuc2_then_la1 + lda #$ff + cmp RASTER + bne b3 + // [8] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + b4_from_b3: + jmp b4 + // main::@4 + b4: + // [9] call loop + // [21] phi from main::@4 to loop [phi:main::@4->loop] + loop_from_b4: + jsr loop + // [10] phi from main::@4 to main::@5 [phi:main::@4->main::@5] + b5_from_b4: + jmp b5 + // main::@5 + b5: + // [11] call zpLoop + // [15] phi from main::@5 to zpLoop [phi:main::@5->zpLoop] + zpLoop_from_b5: + jsr zpLoop + jmp b6 + // main::@6 + b6: + // [12] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta BGCOL + jmp b3 + // main::@2 + b2: + // [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda zpCodeData,x + sta zpCode,x + // [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} +.segment ZpCode + // zpLoop +zpLoop: { + // [16] phi from zpLoop to zpLoop::@1 [phi:zpLoop->zpLoop::@1] + b1_from_zpLoop: + // [16] phi (byte) zpLoop::i#2 = (byte) 0 [phi:zpLoop->zpLoop::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // [16] phi from zpLoop::@1 to zpLoop::@1 [phi:zpLoop::@1->zpLoop::@1] + b1_from_b1: + // [16] phi (byte) zpLoop::i#2 = (byte) zpLoop::i#1 [phi:zpLoop::@1->zpLoop::@1#0] -- register_copy + jmp b1 + // zpLoop::@1 + b1: + // [17] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + // [18] (byte) zpLoop::i#1 ← ++ (byte) zpLoop::i#2 -- vbuxx=_inc_vbuxx + inx + // [19] if((byte) zpLoop::i#1!=(byte) $65) goto zpLoop::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1_from_b1 + jmp breturn + // zpLoop::@return + breturn: + // [20] return + rts +} +.segment Code + // loop +// Code in "normal" memory +loop: { + // [22] phi from loop to loop::@1 [phi:loop->loop::@1] + b1_from_loop: + // [22] phi (byte) loop::i#2 = (byte) 0 [phi:loop->loop::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // [22] phi from loop::@1 to loop::@1 [phi:loop::@1->loop::@1] + b1_from_b1: + // [22] phi (byte) loop::i#2 = (byte) loop::i#1 [phi:loop::@1->loop::@1#0] -- register_copy + jmp b1 + // loop::@1 + b1: + // [23] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BGCOL + // [24] (byte) loop::i#1 ← ++ (byte) loop::i#2 -- vbuxx=_inc_vbuxx + inx + // [25] if((byte) loop::i#1!=(byte) $65) goto loop::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1_from_b1 + jmp breturn + // loop::@return + breturn: + // [26] return + rts +} + // File Data +.segment Data +// Array containing the zeropage code to be transferred to zeropage before execution +zpCodeData: +.segmentout [segments="ZpCode"] + + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp b4 +Removing instruction jmp b5 +Removing instruction jmp b6 +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b1 with b1 +Replacing label b1_from_b1 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Removing instruction b4_from_b3: +Removing instruction loop_from_b4: +Removing instruction b5_from_b4: +Removing instruction zpLoop_from_b5: +Removing instruction b1_from_b1: +Removing instruction b1_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bbegin: +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b4: +Removing instruction b5: +Removing instruction b6: +Removing instruction b1_from_b2: +Removing instruction b1_from_zpLoop: +Removing instruction breturn: +Removing instruction b1_from_loop: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53280 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = (byte*) 53266 +(void()) loop() +(label) loop::@1 +(label) loop::@return +(byte) loop::i +(byte) loop::i#1 reg byte x 151.5 +(byte) loop::i#2 reg byte x 101.0 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 18.333333333333332 +(byte*) main::zpCode +(const byte*) main::zpCode#0 zpCode = (byte*)&(void()) zpLoop() +(byte[]) zpCodeData +(const byte[]) zpCodeData#0 zpCodeData = kickasm {{ .segmentout [segments="ZpCode"] + }} +(void()) zpLoop() +(label) zpLoop::@1 +(label) zpLoop::@return +(byte) zpLoop::i +(byte) zpLoop::i#1 reg byte x 151.5 +(byte) zpLoop::i#2 reg byte x 101.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte x [ zpLoop::i#2 zpLoop::i#1 ] +reg byte x [ loop::i#2 loop::i#1 ] + + +FINAL ASSEMBLER +Score: 4076 + + // File Comments +// Example showing how to use KickAsm segments to compile meant to be transfered to zeropage before execution. +// The linker-file defines the ZpCode segment to be on zeropage and does not include it directly in the PRG-file (by excluding it from the Program segment). +// Instead the compiled code is added as an array of bytes in "normal" memory - and transferred to zeropage at the start of the program + // Upstart + .file [name="zpcode.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$0810] +.segmentdef Data [startAfter="Code"] +.segmentdef ZpCode [start=$80] +.segment Basic +:BasicUpstart(main) + + + // Global Constants & labels + .label RASTER = $d012 + .label BGCOL = $d020 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end +.segment Code + // main +main: { + .label zpCode = zpLoop + // asm + // asm { sei } + sei + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // main::@1 + b1: + // for(char i=0;i<20;i++) + // [6] if((byte) main::i#2<(byte) $14) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$14 + bcc b2 + // main::@3 + b3: + // while(*RASTER!=0xff) + // [7] if(*((const byte*) RASTER#0)!=(byte) $ff) goto main::@3 -- _deref_pbuc1_neq_vbuc2_then_la1 + lda #$ff + cmp RASTER + bne b3 + // [8] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + // main::@4 + // loop() + // [9] call loop + // [21] phi from main::@4 to loop [phi:main::@4->loop] + jsr loop + // [10] phi from main::@4 to main::@5 [phi:main::@4->main::@5] + // main::@5 + // zpLoop() + // [11] call zpLoop + // [15] phi from main::@5 to zpLoop [phi:main::@5->zpLoop] + jsr zpLoop + // main::@6 + // *BGCOL = 0 + // [12] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2 + lda #0 + sta BGCOL + jmp b3 + // main::@2 + b2: + // zpCode[i] = zpCodeData[i] + // [13] *((const byte*) main::zpCode#0 + (byte) main::i#2) ← *((const byte[]) zpCodeData#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda zpCodeData,x + sta zpCode,x + // for(char i=0;i<20;i++) + // [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} +.segment ZpCode + // zpLoop +zpLoop: { + // [16] phi from zpLoop to zpLoop::@1 [phi:zpLoop->zpLoop::@1] + // [16] phi (byte) zpLoop::i#2 = (byte) 0 [phi:zpLoop->zpLoop::@1#0] -- vbuxx=vbuc1 + ldx #0 + // [16] phi from zpLoop::@1 to zpLoop::@1 [phi:zpLoop::@1->zpLoop::@1] + // [16] phi (byte) zpLoop::i#2 = (byte) zpLoop::i#1 [phi:zpLoop::@1->zpLoop::@1#0] -- register_copy + // zpLoop::@1 + b1: + // (*BGCOL)++; + // [17] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + // for(char i:0..100) + // [18] (byte) zpLoop::i#1 ← ++ (byte) zpLoop::i#2 -- vbuxx=_inc_vbuxx + inx + // [19] if((byte) zpLoop::i#1!=(byte) $65) goto zpLoop::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1 + // zpLoop::@return + // } + // [20] return + rts +} +.segment Code + // loop +// Code in "normal" memory +loop: { + // [22] phi from loop to loop::@1 [phi:loop->loop::@1] + // [22] phi (byte) loop::i#2 = (byte) 0 [phi:loop->loop::@1#0] -- vbuxx=vbuc1 + ldx #0 + // [22] phi from loop::@1 to loop::@1 [phi:loop::@1->loop::@1] + // [22] phi (byte) loop::i#2 = (byte) loop::i#1 [phi:loop::@1->loop::@1#0] -- register_copy + // loop::@1 + b1: + // (*BGCOL)--; + // [23] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BGCOL + // for(char i:0..100) + // [24] (byte) loop::i#1 ← ++ (byte) loop::i#2 -- vbuxx=_inc_vbuxx + inx + // [25] if((byte) loop::i#1!=(byte) $65) goto loop::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$65 + bne b1 + // loop::@return + // } + // [26] return + rts +} + // File Data +.segment Data +// Array containing the zeropage code to be transferred to zeropage before execution +zpCodeData: +.segmentout [segments="ZpCode"] + + diff --git a/src/test/ref/examples/zpcode/zpcode.sym b/src/test/ref/examples/zpcode/zpcode.sym new file mode 100644 index 000000000..833e67d67 --- /dev/null +++ b/src/test/ref/examples/zpcode/zpcode.sym @@ -0,0 +1,38 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53280 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = (byte*) 53266 +(void()) loop() +(label) loop::@1 +(label) loop::@return +(byte) loop::i +(byte) loop::i#1 reg byte x 151.5 +(byte) loop::i#2 reg byte x 101.0 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 18.333333333333332 +(byte*) main::zpCode +(const byte*) main::zpCode#0 zpCode = (byte*)&(void()) zpLoop() +(byte[]) zpCodeData +(const byte[]) zpCodeData#0 zpCodeData = kickasm {{ .segmentout [segments="ZpCode"] + }} +(void()) zpLoop() +(label) zpLoop::@1 +(label) zpLoop::@return +(byte) zpLoop::i +(byte) zpLoop::i#1 reg byte x 151.5 +(byte) zpLoop::i#2 reg byte x 101.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte x [ zpLoop::i#2 zpLoop::i#1 ] +reg byte x [ loop::i#2 loop::i#1 ]