diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index db212128e..20fbcc2d0 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -1011,6 +1011,11 @@ public class TestPrograms { assertError("cast-error.c", "Type mismatch"); } + @Test + public void testInlineAsmMaVar() throws IOException, URISyntaxException { + compileAndCompare("inline-asm-ma-var.c"); + } + @Test public void testInlineAsmParam() throws IOException, URISyntaxException { compileAndCompare("inline-asm-param.c"); diff --git a/src/test/kc/inline-asm-ma-var.c b/src/test/kc/inline-asm-ma-var.c new file mode 100644 index 000000000..6cfa8d3a5 --- /dev/null +++ b/src/test/kc/inline-asm-ma-var.c @@ -0,0 +1,17 @@ +// Test access to __ma variable from inline ASM + +char * const SCREEN = 0x0400; + +void main() { + __ma char value = 0; + for(char i=0;i<10;i++) { + + value += i; + + asm { + lda #$55 + eor value + sta SCREEN + } + } +} diff --git a/src/test/ref/inline-asm-ma-var.asm b/src/test/ref/inline-asm-ma-var.asm new file mode 100644 index 000000000..d5739adc9 --- /dev/null +++ b/src/test/ref/inline-asm-ma-var.asm @@ -0,0 +1,31 @@ +// Test access to __ma variable from inline ASM +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + .label value = 2 + // value = 0 + lda #0 + sta.z value + tax + __b1: + // for(char i=0;i<10;i++) + cpx #$a + bcc __b2 + // } + rts + __b2: + // value += i + txa + clc + adc.z value + sta.z value + // asm + lda #$55 + eor value + sta SCREEN + // for(char i=0;i<10;i++) + inx + jmp __b1 +} diff --git a/src/test/ref/inline-asm-ma-var.cfg b/src/test/ref/inline-asm-ma-var.cfg new file mode 100644 index 000000000..ab97fb57a --- /dev/null +++ b/src/test/ref/inline-asm-ma-var.cfg @@ -0,0 +1,26 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] (byte) main::value ← (byte) 0 + 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) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 + asm { lda#$55 eorvalue staSCREEN } + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/inline-asm-ma-var.log b/src/test/ref/inline-asm-ma-var.log new file mode 100644 index 000000000..529327b08 --- /dev/null +++ b/src/test/ref/inline-asm-ma-var.log @@ -0,0 +1,375 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + (byte) main::value ← (byte) 0 + (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) $a + 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::value ← (byte) main::value + (byte) main::i#3 + asm { lda#$55 eorvalue staSCREEN } + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*)(number) $400 +(void()) main() +(bool~) main::$0 +(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 +(byte) main::value loadstore + +Adding number conversion cast (unumber) $a in (bool~) main::$0 ← (byte) main::i#2 < (number) $a +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $a +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $a +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [4] if((byte) main::i#2<(byte) $a) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [12] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +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() + +(void()) main() +main: scope:[main] from @1 + [4] (byte) main::value ← (byte) 0 + 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) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 + asm { lda#$55 eorvalue staSCREEN } + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte) main::i +(byte) main::i#1 202.0 +(byte) main::i#2 101.0 +(byte) main::value loadstore 35.5 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::value to live range equivalence class [ main::value ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::value ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] +Allocated zp[1]:3 [ main::value ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test access to __ma variable from inline ASM + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: + // main +main: { + .label value = 3 + .label i = 2 + // [4] (byte) main::value ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z value + // [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) $a) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$a + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [7] return + rts + // main::@2 + __b2: + // [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 -- vbuz1=vbuz1_plus_vbuz2 + lda.z value + clc + adc.z i + sta.z value + // asm { lda#$55 eorvalue staSCREEN } + lda #$55 + eor value + sta SCREEN + // [10] (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 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] (byte) main::value ← (byte) 0 [ main::value ] ( main:2 [ main::value ] { } ) always clobbers reg byte a +Statement [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 [ main::value main::i#2 ] ( main:2 [ main::value main::i#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 asm { lda#$55 eorvalue staSCREEN } always clobbers reg byte a +Statement [4] (byte) main::value ← (byte) 0 [ main::value ] ( main:2 [ main::value ] { } ) always clobbers reg byte a +Statement [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 [ main::value main::i#2 ] ( main:2 [ main::value main::i#2 ] { } ) always clobbers reg byte a +Statement asm { lda#$55 eorvalue staSCREEN } 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::value ] : zp[1]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 303: zp[1]:2 [ main::i#2 main::i#1 ] 35.5: zp[1]:3 [ main::value ] +Uplift Scope [] + +Uplifting [main] best 398 combination reg byte x [ main::i#2 main::i#1 ] zp[1]:3 [ main::value ] +Uplifting [] best 398 combination +Attempting to uplift remaining variables inzp[1]:3 [ main::value ] +Uplifting [main] best 398 combination zp[1]:3 [ main::value ] +Allocated (was zp[1]:3) zp[1]:2 [ main::value ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test access to __ma variable from inline ASM + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: + // main +main: { + .label value = 2 + // [4] (byte) main::value ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z value + // [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) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [7] return + rts + // main::@2 + __b2: + // [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 -- vbuz1=vbuz1_plus_vbuxx + txa + clc + adc.z value + sta.z value + // asm { lda#$55 eorvalue staSCREEN } + lda #$55 + eor value + sta SCREEN + // [10] (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 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Replacing instruction ldx #0 with TAX + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 202.0 +(byte) main::i#2 reg byte x 101.0 +(byte) main::value loadstore zp[1]:2 35.5 + +reg byte x [ main::i#2 main::i#1 ] +zp[1]:2 [ main::value ] + + +FINAL ASSEMBLER +Score: 326 + + // File Comments +// Test access to __ma variable from inline ASM + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label value = 2 + // value = 0 + // [4] (byte) main::value ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z value + // [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 + tax + // main::@1 + __b1: + // for(char i=0;i<10;i++) + // [6] if((byte) main::i#2<(byte) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc __b2 + // main::@return + // } + // [7] return + rts + // main::@2 + __b2: + // value += i + // [8] (byte) main::value ← (byte) main::value + (byte) main::i#2 -- vbuz1=vbuz1_plus_vbuxx + txa + clc + adc.z value + sta.z value + // asm + // asm { lda#$55 eorvalue staSCREEN } + lda #$55 + eor value + sta SCREEN + // for(char i=0;i<10;i++) + // [10] (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 +} + // File Data + diff --git a/src/test/ref/inline-asm-ma-var.sym b/src/test/ref/inline-asm-ma-var.sym new file mode 100644 index 000000000..0f53ae2f2 --- /dev/null +++ b/src/test/ref/inline-asm-ma-var.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 202.0 +(byte) main::i#2 reg byte x 101.0 +(byte) main::value loadstore zp[1]:2 35.5 + +reg byte x [ main::i#2 main::i#1 ] +zp[1]:2 [ main::value ]