diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index 000bf3a8a..0e208ebd4 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -34,7 +34,7 @@ import java.util.stream.Collectors; descriptionHeading = "%nDescription:%n%n", parameterListHeading = "%nParameters:%n", optionListHeading = "%nOptions:%n", - version = "KickC 0.8.3 BETA" + version = "KickC 0.8.4 BETA" ) public class KickC implements Callable { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 9aaffcb22..9fe76b8bc 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -2214,6 +2214,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor1 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + ldx #0 + __b1: + // for(char i=0;i<5;i++) + cpx #5 + bcc __b2 + // } + rts + __b2: + // x[i] += ( v[i] += 5 ) + txa + asl + tay + // v[i] += 5 + clc + lda v,y + adc #5 + sta v,y + lda v+1,y + adc #0 + sta v+1,y + // x[i] += ( v[i] += 5 ) + clc + lda x,y + adc v,y + sta x,y + lda x+1,y + adc v+1,y + sta x+1,y + // for(char i=0;i<5;i++) + inx + jmp __b1 +} + v: .fill 2*5, 0 + x: .fill 2*5, 0 diff --git a/src/test/ref/inner-index-problem.cfg b/src/test/ref/inner-index-problem.cfg new file mode 100644 index 000000000..0d7e8c108 --- /dev/null +++ b/src/test/ref/inner-index-problem.cfg @@ -0,0 +1,18 @@ + +(void()) main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [2] if((byte) main::i#2<(byte) 5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 + [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 + [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/inner-index-problem.log b/src/test/ref/inner-index-problem.log new file mode 100644 index 000000000..f5ac2e073 --- /dev/null +++ b/src/test/ref/inner-index-problem.log @@ -0,0 +1,358 @@ + +CONTROL FLOW GRAPH SSA + +(void()) main() +main: scope:[main] from __start + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + (bool~) main::$0 ← (byte) main::i#2 < (number) 5 + if((bool~) main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (byte~) main::$1 ← (byte) main::i#3 * (const byte) SIZEOF_WORD + *((const word*) v + (byte~) main::$1) ← *((const word*) v + (byte~) main::$1) + (number) 5 + (byte~) main::$2 ← (byte) main::i#3 * (const byte) SIZEOF_WORD + *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return + +(void()) __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +(const byte) SIZEOF_WORD = (byte) 2 +(void()) __start() +(label) __start::@1 +(label) __start::@return +(void()) main() +(bool~) main::$0 +(byte~) main::$1 +(byte~) main::$2 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(const word*) v[(number) 5] = { fill( 5, 0) } +(const word*) x[(number) 5] = { fill( 5, 0) } + +Adding number conversion cast (unumber) 5 in (bool~) main::$0 ← (byte) main::i#2 < (number) 5 +Adding number conversion cast (unumber) 5 in *((const word*) v + (byte~) main::$1) ← *((const word*) v + (byte~) main::$1) + (number) 5 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 5 +Simplifying constant integer cast 5 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 5 +Finalized unsigned number type (byte) 5 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Identified duplicate assignment right side [7] (byte~) main::$2 ← (byte) main::i#2 * (const byte) SIZEOF_WORD +Successful SSA optimization Pass2DuplicateRValueIdentification +Simple Condition (bool~) main::$0 [3] if((byte) main::i#2<(byte) 5) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Alias main::$2 = main::$1 +Successful SSA optimization Pass2AliasElimination +Rewriting multiplication to use shift [2] (byte~) main::$2 ← (byte) main::i#2 * (const byte) SIZEOF_WORD +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Eliminating unused constant (const byte) SIZEOF_WORD +Successful SSA optimization PassNEliminateUnusedVars +Adding NOP phi() at start of main +CALL GRAPH + +Created 1 initial phi equivalence classes +Coalesced [8] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +(void()) main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [2] if((byte) main::i#2<(byte) 5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 + [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 + [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte~) main::$2 33.0 +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 8.8 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::$2 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] +Allocated zp[1]:3 [ main::$2 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Demonstrates a problem with inner indexes into arrays where the elemt size>1 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // main +main: { + .label __2 = 3 + .label i = 2 + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) main::i#2<(byte) 5) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #5 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda.z i + asl + sta.z __2 + // [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 -- pwuc1_derefidx_vbuz1=pwuc1_derefidx_vbuz1_plus_vbuc2 + ldy.z __2 + clc + lda v,y + adc #5 + sta v,y + lda v+1,y + adc #0 + sta v+1,y + // [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) -- pwuc1_derefidx_vbuz1=pwuc1_derefidx_vbuz1_plus_pwuc2_derefidx_vbuz1 + ldy.z __2 + clc + lda x,y + adc v,y + sta x,y + lda x+1,y + adc v+1,y + sta x+1,y + // [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + v: .fill 2*5, 0 + x: .fill 2*5, 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] +Statement [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::$2 ] +Statement [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { } ) always clobbers reg byte a +Statement [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { } ) always clobbers reg byte a +Statement [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::$2 ] : zp[1]:3 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 33: zp[1]:3 [ main::$2 ] 30.8: zp[1]:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 791 combination reg byte y [ main::$2 ] reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 791 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates a problem with inner indexes into arrays where the elemt size>1 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // main::@1 + __b1: + // [2] if((byte) main::i#2<(byte) 5) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #5 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + // [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 -- pwuc1_derefidx_vbuyy=pwuc1_derefidx_vbuyy_plus_vbuc2 + clc + lda v,y + adc #5 + sta v,y + lda v+1,y + adc #0 + sta v+1,y + // [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) -- pwuc1_derefidx_vbuyy=pwuc1_derefidx_vbuyy_plus_pwuc2_derefidx_vbuyy + clc + lda x,y + adc v,y + sta x,y + lda x+1,y + adc v+1,y + sta x+1,y + // [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + v: .fill 2*5, 0 + x: .fill 2*5, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(void()) main() +(byte~) main::$2 reg byte y 33.0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 8.8 +(const word*) v[(number) 5] = { fill( 5, 0) } +(const word*) x[(number) 5] = { fill( 5, 0) } + +reg byte x [ main::i#2 main::i#1 ] +reg byte y [ main::$2 ] + + +FINAL ASSEMBLER +Score: 731 + + // File Comments +// Demonstrates a problem with inner indexes into arrays where the elemt size>1 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // main::@1 + __b1: + // for(char i=0;i<5;i++) + // [2] if((byte) main::i#2<(byte) 5) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #5 + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // x[i] += ( v[i] += 5 ) + // [4] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + // v[i] += 5 + // [5] *((const word*) v + (byte~) main::$2) ← *((const word*) v + (byte~) main::$2) + (byte) 5 -- pwuc1_derefidx_vbuyy=pwuc1_derefidx_vbuyy_plus_vbuc2 + clc + lda v,y + adc #5 + sta v,y + lda v+1,y + adc #0 + sta v+1,y + // x[i] += ( v[i] += 5 ) + // [6] *((const word*) x + (byte~) main::$2) ← *((const word*) x + (byte~) main::$2) + *((const word*) v + (byte~) main::$2) -- pwuc1_derefidx_vbuyy=pwuc1_derefidx_vbuyy_plus_pwuc2_derefidx_vbuyy + clc + lda x,y + adc v,y + sta x,y + lda x+1,y + adc v+1,y + sta x+1,y + // for(char i=0;i<5;i++) + // [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + v: .fill 2*5, 0 + x: .fill 2*5, 0 + diff --git a/src/test/ref/inner-index-problem.sym b/src/test/ref/inner-index-problem.sym new file mode 100644 index 000000000..37439835b --- /dev/null +++ b/src/test/ref/inner-index-problem.sym @@ -0,0 +1,13 @@ +(void()) main() +(byte~) main::$2 reg byte y 33.0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 8.8 +(const word*) v[(number) 5] = { fill( 5, 0) } +(const word*) x[(number) 5] = { fill( 5, 0) } + +reg byte x [ main::i#2 main::i#1 ] +reg byte y [ main::$2 ]