1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-27 19:50:10 +00:00

Optimized live ranges by statement idx in PassNLiveRangeVariables. Added example of how to use segments to compile code meant for transfer to zeropage.

This commit is contained in:
jespergravgaard 2019-09-07 18:23:11 +02:00
parent 33c39bfd52
commit 7fb7d3acf6
10 changed files with 1201 additions and 1 deletions

View File

@ -33,6 +33,14 @@ public class LiveRange {
return s;
}
/**
* Get the underlying statement intervals
* @return The intervals
*/
public List<LiveInterval> getIntervals() {
return intervals;
}
/**
* Add an index to the live range
*

View File

@ -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<VariableRef, LiveRange> 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<List<VariableRef>> aliveByStatementIdx;
public LiveRangeVariablesByStatement() {
this.aliveByStatementIdx = new ArrayList<>();
}
public void addAlive(int statementIdx, VariableRef variableRef) {
while(statementIdx>=aliveByStatementIdx.size()) {
aliveByStatementIdx.add(new ArrayList<>());
}
List<VariableRef> variableRefs = aliveByStatementIdx.get(statementIdx);
variableRefs.add(variableRef);
}
public List<VariableRef> getAlive(int statementIdx) {
if(statementIdx>=aliveByStatementIdx.size())
return new ArrayList<>();
return aliveByStatementIdx.get(statementIdx);
}
}

View File

@ -80,9 +80,10 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
private boolean calculateLiveRanges(LiveRangeVariables liveRanges) {
VariableReferenceInfos referenceInfo = getProgram().getVariableReferenceInfos();
boolean modified = false;
LiveRangeVariables.LiveRangeVariablesByStatement liveRangeVariablesByStatement = liveRanges.getLiveRangeVariablesByStatement();
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for(Statement stmt : block.getStatements()) {
List<VariableRef> aliveNextStmt = liveRanges.getAlive(stmt.getIndex());
List<VariableRef> aliveNextStmt = liveRangeVariablesByStatement.getAlive(stmt.getIndex());
Collection<VariableRef> definedNextStmt = referenceInfo.getDefinedVars(stmt);
initLiveRange(liveRanges, definedNextStmt);
Collection<PreviousStatement> previousStmts = getPreviousStatements(stmt);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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