diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index a434fa543..2b3031f24 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,11 +42,31 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testLibraryConstructorError2() throws IOException, URISyntaxException { + assertError("library-constructor-error-2.c", "Error! Procedure not found print"); + } + + @Test + public void testLibraryConstructorError1() throws IOException, URISyntaxException { + assertError("library-constructor-error-1.c", "Error! Constructor procedure not found my_init"); + } + + @Test + public void testLibraryConstructorError0() throws IOException, URISyntaxException { + assertError("library-constructor-error-0.c", "Error! #pragma constructor_for requires at least 2 parameters."); + } + @Test public void testLibraryConstructor2() throws IOException, URISyntaxException { compileAndCompare("library-constructor-2.c"); } + @Test + public void testLibraryConstructor3() throws IOException, URISyntaxException { + compileAndCompare("library-constructor-3.c", log()); + } + @Test public void testLibraryConstructor1() throws IOException, URISyntaxException { compileAndCompare("library-constructor-1.c"); diff --git a/src/test/kc/library-constructor-3.c b/src/test/kc/library-constructor-3.c new file mode 100644 index 000000000..1437d5f6d --- /dev/null +++ b/src/test/kc/library-constructor-3.c @@ -0,0 +1,25 @@ +// Demonstrates Library Constructor Functionality +// Multiple #pragma constructor_for() constructors + +#pragma constructor_for(init_1, print) +#pragma constructor_for(init_2, print) + +volatile char sym; +char * volatile SCREEN; + +void init_1(void) { + sym = '*'; +} + +void init_2(void) { + SCREEN = 0x0400; +} + +void main(void) { + print(); +} + +void print() { + *SCREEN = sym; +} + diff --git a/src/test/kc/library-constructor-error-0.c b/src/test/kc/library-constructor-error-0.c new file mode 100644 index 000000000..88f7fd7a6 --- /dev/null +++ b/src/test/kc/library-constructor-error-0.c @@ -0,0 +1,14 @@ +// Demonstrates Library Constructor Functionality +// To few parameters for pragma + +#pragma constructor_for(my_init) + +// Constructor +void my_init(void) { +} + +char * const SCREEN = 0x0400; + +void main(void) { +} + diff --git a/src/test/kc/library-constructor-error-1.c b/src/test/kc/library-constructor-error-1.c new file mode 100644 index 000000000..3a62e0040 --- /dev/null +++ b/src/test/kc/library-constructor-error-1.c @@ -0,0 +1,10 @@ +// Demonstrates Library Constructor Functionality +// Init procedure not found + +#pragma constructor_for(my_init, main) + +char * const SCREEN = 0x0400; + +void main(void) { +} + diff --git a/src/test/kc/library-constructor-error-2.c b/src/test/kc/library-constructor-error-2.c new file mode 100644 index 000000000..683bf67d0 --- /dev/null +++ b/src/test/kc/library-constructor-error-2.c @@ -0,0 +1,14 @@ +// Demonstrates Library Constructor Functionality +// Init procedure not found + +#pragma constructor_for(my_init, print) + +char * const SCREEN = 0x0400; + +// Constructor +void my_init(void) { +} + +void main(void) { +} + diff --git a/src/test/ref/library-constructor-3.asm b/src/test/ref/library-constructor-3.asm new file mode 100644 index 000000000..e78abbf52 --- /dev/null +++ b/src/test/ref/library-constructor-3.asm @@ -0,0 +1,53 @@ +// Demonstrates Library Constructor Functionality +// Multiple #pragma constructor_for() constructors +.pc = $801 "Basic" +:BasicUpstart(__start) +.pc = $80d "Program" + .label sym = 2 + .label SCREEN = 3 +__start: { + // sym + lda #0 + sta.z sym + // SCREEN + sta.z SCREEN + sta.z SCREEN+1 + // #pragma constructor_for(init_1, print) + //#pragma constructor + jsr init_1 + // #pragma constructor_for(init_2, print) + //#pragma constructor + jsr init_2 + jsr main + rts +} +init_2: { + // SCREEN = 0x0400 + lda #<$400 + sta.z SCREEN + lda #>$400 + sta.z SCREEN+1 + // } + rts +} +init_1: { + // sym = '*' + lda #'*' + sta.z sym + // } + rts +} +main: { + // print() + jsr print + // } + rts +} +print: { + // *SCREEN = sym + lda.z sym + ldy #0 + sta (SCREEN),y + // } + rts +} diff --git a/src/test/ref/library-constructor-3.cfg b/src/test/ref/library-constructor-3.cfg new file mode 100644 index 000000000..00b6535e4 --- /dev/null +++ b/src/test/ref/library-constructor-3.cfg @@ -0,0 +1,54 @@ + +(void()) __start() +__start: scope:[__start] from + [0] phi() + to:__start::__init1 +__start::__init1: scope:[__start] from __start + [1] (volatile byte) sym ← (byte) 0 + [2] (volatile byte*) SCREEN ← (byte*) 0 + [3] call init_1 + to:__start::@2 +__start::@2: scope:[__start] from __start::__init1 + [4] phi() + [5] call init_2 + to:__start::@1 +__start::@1: scope:[__start] from __start::@2 + [6] phi() + [7] call main + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + [8] return + to:@return + +(void()) init_2() +init_2: scope:[init_2] from __start::@2 + [9] (volatile byte*) SCREEN ← (byte*) 1024 + to:init_2::@return +init_2::@return: scope:[init_2] from init_2 + [10] return + to:@return + +(void()) init_1() +init_1: scope:[init_1] from __start::__init1 + [11] (volatile byte) sym ← (byte) '*' + to:init_1::@return +init_1::@return: scope:[init_1] from init_1 + [12] return + to:@return + +(void()) main() +main: scope:[main] from __start::@1 + [13] phi() + [14] call print + to:main::@return +main::@return: scope:[main] from main + [15] return + to:@return + +(void()) print() +print: scope:[print] from main + [16] *((volatile byte*) SCREEN) ← (volatile byte) sym + to:print::@return +print::@return: scope:[print] from print + [17] return + to:@return diff --git a/src/test/ref/library-constructor-3.log b/src/test/ref/library-constructor-3.log new file mode 100644 index 000000000..46b706613 --- /dev/null +++ b/src/test/ref/library-constructor-3.log @@ -0,0 +1,542 @@ +Inlined call call __init + +CONTROL FLOW GRAPH SSA + +(void()) init_1() +init_1: scope:[init_1] from __start::__init1 + (volatile byte) sym ← (byte) '*' + to:init_1::@return +init_1::@return: scope:[init_1] from init_1 + return + to:@return + +(void()) init_2() +init_2: scope:[init_2] from __start::@2 + (volatile byte*) SCREEN ← ((byte*)) (number) $400 + to:init_2::@return +init_2::@return: scope:[init_2] from init_2 + return + to:@return + +(void()) main() +main: scope:[main] from __start::@1 + call print + to:main::@1 +main::@1: scope:[main] from main + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return + +(void()) print() +print: scope:[print] from main + *((volatile byte*) SCREEN) ← (volatile byte) sym + to:print::@return +print::@return: scope:[print] from print + return + to:@return + +(void()) __start() +__start: scope:[__start] from + to:__start::__init1 +__start::__init1: scope:[__start] from __start + (volatile byte) sym ← (byte) 0 + (volatile byte*) SCREEN ← (byte*) 0 + call init_1 + to:__start::@2 +__start::@2: scope:[__start] from __start::__init1 + call init_2 + to:__start::@3 +__start::@3: scope:[__start] from __start::@2 + to:__start::@1 +__start::@1: scope:[__start] from __start::@3 + call main + to:__start::@4 +__start::@4: scope:[__start] from __start::@1 + to:__start::@return +__start::@return: scope:[__start] from __start::@4 + return + to:@return + +SYMBOL TABLE SSA +(volatile byte*) SCREEN loadstore +(void()) __start() +(label) __start::@1 +(label) __start::@2 +(label) __start::@3 +(label) __start::@4 +(label) __start::@return +(label) __start::__init1 +(void()) init_1() +(label) init_1::@return +(void()) init_2() +(label) init_2::@return +(void()) main() +(label) main::@1 +(label) main::@return +(void()) print() +(label) print::@return +(volatile byte) sym loadstore + +Inlining cast (volatile byte*) SCREEN ← (byte*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Successful SSA optimization PassNCastSimplification +Adding NOP phi() at start of __start +Adding NOP phi() at start of __start::@2 +Adding NOP phi() at start of __start::@3 +Adding NOP phi() at start of __start::@1 +Adding NOP phi() at start of __start::@4 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +CALL GRAPH +Calls in [__start] to init_1:3 init_2:5 main:8 +Calls in [main] to print:16 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) __start::@3 +Culled Empty Block (label) __start::@4 +Culled Empty Block (label) main::@1 +Adding NOP phi() at start of __start +Adding NOP phi() at start of __start::@2 +Adding NOP phi() at start of __start::@1 +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +(void()) __start() +__start: scope:[__start] from + [0] phi() + to:__start::__init1 +__start::__init1: scope:[__start] from __start + [1] (volatile byte) sym ← (byte) 0 + [2] (volatile byte*) SCREEN ← (byte*) 0 + [3] call init_1 + to:__start::@2 +__start::@2: scope:[__start] from __start::__init1 + [4] phi() + [5] call init_2 + to:__start::@1 +__start::@1: scope:[__start] from __start::@2 + [6] phi() + [7] call main + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + [8] return + to:@return + +(void()) init_2() +init_2: scope:[init_2] from __start::@2 + [9] (volatile byte*) SCREEN ← (byte*) 1024 + to:init_2::@return +init_2::@return: scope:[init_2] from init_2 + [10] return + to:@return + +(void()) init_1() +init_1: scope:[init_1] from __start::__init1 + [11] (volatile byte) sym ← (byte) '*' + to:init_1::@return +init_1::@return: scope:[init_1] from init_1 + [12] return + to:@return + +(void()) main() +main: scope:[main] from __start::@1 + [13] phi() + [14] call print + to:main::@return +main::@return: scope:[main] from main + [15] return + to:@return + +(void()) print() +print: scope:[print] from main + [16] *((volatile byte*) SCREEN) ← (volatile byte) sym + to:print::@return +print::@return: scope:[print] from print + [17] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(volatile byte*) SCREEN loadstore 22.8 +(void()) __start() +(void()) init_1() +(void()) init_2() +(void()) main() +(void()) print() +(volatile byte) sym loadstore 16.285714285714285 + +Initial phi equivalence classes +Added variable sym to live range equivalence class [ sym ] +Added variable SCREEN to live range equivalence class [ SCREEN ] +Complete equivalence classes +[ sym ] +[ SCREEN ] +Allocated zp[1]:2 [ sym ] +Allocated zp[2]:3 [ SCREEN ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Demonstrates Library Constructor Functionality +// Multiple #pragma constructor_for() constructors + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__start) +.pc = $80d "Program" + // Global Constants & labels + .label sym = 2 + .label SCREEN = 3 + // __start +__start: { + jmp __init1 + // __start::__init1 + __init1: + // [1] (volatile byte) sym ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z sym + // [2] (volatile byte*) SCREEN ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z SCREEN + lda #>0 + sta.z SCREEN+1 + // [3] call init_1 + //#pragma constructor + jsr init_1 + // [4] phi from __start::__init1 to __start::@2 [phi:__start::__init1->__start::@2] + __b2_from___init1: + jmp __b2 + // __start::@2 + __b2: + // [5] call init_2 + //#pragma constructor + jsr init_2 + // [6] phi from __start::@2 to __start::@1 [phi:__start::@2->__start::@1] + __b1_from___b2: + jmp __b1 + // __start::@1 + __b1: + // [7] call main + // [13] phi from __start::@1 to main [phi:__start::@1->main] + main_from___b1: + jsr main + jmp __breturn + // __start::@return + __breturn: + // [8] return + rts +} + // init_2 +init_2: { + // [9] (volatile byte*) SCREEN ← (byte*) 1024 -- pbuz1=pbuc1 + lda #<$400 + sta.z SCREEN + lda #>$400 + sta.z SCREEN+1 + jmp __breturn + // init_2::@return + __breturn: + // [10] return + rts +} + // init_1 +init_1: { + // [11] (volatile byte) sym ← (byte) '*' -- vbuz1=vbuc1 + lda #'*' + sta.z sym + jmp __breturn + // init_1::@return + __breturn: + // [12] return + rts +} + // main +main: { + // [14] call print + jsr print + jmp __breturn + // main::@return + __breturn: + // [15] return + rts +} + // print +print: { + // [16] *((volatile byte*) SCREEN) ← (volatile byte) sym -- _deref_pbuz1=vbuz2 + lda.z sym + ldy #0 + sta (SCREEN),y + jmp __breturn + // print::@return + __breturn: + // [17] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] (volatile byte) sym ← (byte) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [2] (volatile byte*) SCREEN ← (byte*) 0 [ ] ( [ ] { } ) always clobbers reg byte a +Statement [9] (volatile byte*) SCREEN ← (byte*) 1024 [ SCREEN ] ( [ SCREEN ] { } init_2:5 [ sym SCREEN ] { } ) always clobbers reg byte a +Statement [11] (volatile byte) sym ← (byte) '*' [ sym ] ( [ sym ] { } init_1:3 [ sym ] { } ) always clobbers reg byte a +Statement [16] *((volatile byte*) SCREEN) ← (volatile byte) sym [ ] ( main:7::print:14 [ ] { } ) always clobbers reg byte a reg byte y +Potential registers zp[1]:2 [ sym ] : zp[1]:2 , +Potential registers zp[2]:3 [ SCREEN ] : zp[2]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [] 22.8: zp[2]:3 [ SCREEN ] 16.29: zp[1]:2 [ sym ] +Uplift Scope [init_1] +Uplift Scope [init_2] +Uplift Scope [main] +Uplift Scope [print] +Uplift Scope [__start] + +Uplifting [] best 146 combination zp[2]:3 [ SCREEN ] zp[1]:2 [ sym ] +Uplifting [init_1] best 146 combination +Uplifting [init_2] best 146 combination +Uplifting [main] best 146 combination +Uplifting [print] best 146 combination +Uplifting [__start] best 146 combination +Attempting to uplift remaining variables inzp[1]:2 [ sym ] +Uplifting [] best 146 combination zp[1]:2 [ sym ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates Library Constructor Functionality +// Multiple #pragma constructor_for() constructors + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__start) +.pc = $80d "Program" + // Global Constants & labels + .label sym = 2 + .label SCREEN = 3 + // __start +__start: { + jmp __init1 + // __start::__init1 + __init1: + // [1] (volatile byte) sym ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z sym + // [2] (volatile byte*) SCREEN ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z SCREEN + lda #>0 + sta.z SCREEN+1 + // [3] call init_1 + //#pragma constructor + jsr init_1 + // [4] phi from __start::__init1 to __start::@2 [phi:__start::__init1->__start::@2] + __b2_from___init1: + jmp __b2 + // __start::@2 + __b2: + // [5] call init_2 + //#pragma constructor + jsr init_2 + // [6] phi from __start::@2 to __start::@1 [phi:__start::@2->__start::@1] + __b1_from___b2: + jmp __b1 + // __start::@1 + __b1: + // [7] call main + // [13] phi from __start::@1 to main [phi:__start::@1->main] + main_from___b1: + jsr main + jmp __breturn + // __start::@return + __breturn: + // [8] return + rts +} + // init_2 +init_2: { + // [9] (volatile byte*) SCREEN ← (byte*) 1024 -- pbuz1=pbuc1 + lda #<$400 + sta.z SCREEN + lda #>$400 + sta.z SCREEN+1 + jmp __breturn + // init_2::@return + __breturn: + // [10] return + rts +} + // init_1 +init_1: { + // [11] (volatile byte) sym ← (byte) '*' -- vbuz1=vbuc1 + lda #'*' + sta.z sym + jmp __breturn + // init_1::@return + __breturn: + // [12] return + rts +} + // main +main: { + // [14] call print + jsr print + jmp __breturn + // main::@return + __breturn: + // [15] return + rts +} + // print +print: { + // [16] *((volatile byte*) SCREEN) ← (volatile byte) sym -- _deref_pbuz1=vbuz2 + lda.z sym + ldy #0 + sta (SCREEN),y + jmp __breturn + // print::@return + __breturn: + // [17] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __init1 +Removing instruction jmp __b2 +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #<0 +Removing instruction lda #>0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __b2_from___init1: +Removing instruction __b1_from___b2: +Removing instruction main_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __init1: +Removing instruction __b2: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(volatile byte*) SCREEN loadstore zp[2]:3 22.8 +(void()) __start() +(label) __start::@1 +(label) __start::@2 +(label) __start::@return +(label) __start::__init1 +(void()) init_1() +(label) init_1::@return +(void()) init_2() +(label) init_2::@return +(void()) main() +(label) main::@return +(void()) print() +(label) print::@return +(volatile byte) sym loadstore zp[1]:2 16.285714285714285 + +zp[1]:2 [ sym ] +zp[2]:3 [ SCREEN ] + + +FINAL ASSEMBLER +Score: 91 + + // File Comments +// Demonstrates Library Constructor Functionality +// Multiple #pragma constructor_for() constructors + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__start) +.pc = $80d "Program" + // Global Constants & labels + .label sym = 2 + .label SCREEN = 3 + // __start +__start: { + // __start::__init1 + // sym + // [1] (volatile byte) sym ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z sym + // SCREEN + // [2] (volatile byte*) SCREEN ← (byte*) 0 -- pbuz1=pbuc1 + sta.z SCREEN + sta.z SCREEN+1 + // #pragma constructor_for(init_1, print) + // [3] call init_1 + //#pragma constructor + jsr init_1 + // [4] phi from __start::__init1 to __start::@2 [phi:__start::__init1->__start::@2] + // __start::@2 + // #pragma constructor_for(init_2, print) + // [5] call init_2 + //#pragma constructor + jsr init_2 + // [6] phi from __start::@2 to __start::@1 [phi:__start::@2->__start::@1] + // __start::@1 + // [7] call main + // [13] phi from __start::@1 to main [phi:__start::@1->main] + jsr main + // __start::@return + // [8] return + rts +} + // init_2 +init_2: { + // SCREEN = 0x0400 + // [9] (volatile byte*) SCREEN ← (byte*) 1024 -- pbuz1=pbuc1 + lda #<$400 + sta.z SCREEN + lda #>$400 + sta.z SCREEN+1 + // init_2::@return + // } + // [10] return + rts +} + // init_1 +init_1: { + // sym = '*' + // [11] (volatile byte) sym ← (byte) '*' -- vbuz1=vbuc1 + lda #'*' + sta.z sym + // init_1::@return + // } + // [12] return + rts +} + // main +main: { + // print() + // [14] call print + jsr print + // main::@return + // } + // [15] return + rts +} + // print +print: { + // *SCREEN = sym + // [16] *((volatile byte*) SCREEN) ← (volatile byte) sym -- _deref_pbuz1=vbuz2 + lda.z sym + ldy #0 + sta (SCREEN),y + // print::@return + // } + // [17] return + rts +} + // File Data + diff --git a/src/test/ref/library-constructor-3.sym b/src/test/ref/library-constructor-3.sym new file mode 100644 index 000000000..3c6e2d63a --- /dev/null +++ b/src/test/ref/library-constructor-3.sym @@ -0,0 +1,18 @@ +(volatile byte*) SCREEN loadstore zp[2]:3 22.8 +(void()) __start() +(label) __start::@1 +(label) __start::@2 +(label) __start::@return +(label) __start::__init1 +(void()) init_1() +(label) init_1::@return +(void()) init_2() +(label) init_2::@return +(void()) main() +(label) main::@return +(void()) print() +(label) print::@return +(volatile byte) sym loadstore zp[1]:2 16.285714285714285 + +zp[1]:2 [ sym ] +zp[2]:3 [ SCREEN ]