diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index d7a49dd98..aa9475206 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -137,6 +137,9 @@ public class Compiler { } public Program compile(String fileName) { + if(fileName.endsWith(".kc")) { + fileName = fileName.substring(0, fileName.length()-3); + } program.setFileName(fileName); program.setStatementSequence(new StatementSequence()); try { diff --git a/src/main/java/dk/camelot64/kickc/model/TargetPlatform.java b/src/main/java/dk/camelot64/kickc/model/TargetPlatform.java index a0c639514..03fdca0b4 100644 --- a/src/main/java/dk/camelot64/kickc/model/TargetPlatform.java +++ b/src/main/java/dk/camelot64/kickc/model/TargetPlatform.java @@ -11,7 +11,9 @@ public enum TargetPlatform { /** 6502 assembler (with no upstart code.)*/ ASM6502("asm6502"), /** 6502 assembler (with no upstart code.)*/ - ASM6502_SEGMENTS("asm6502_segments"); + ASM6502_SEGMENTS("asm6502_segments"), + /** Custom target platform specified in a separate linker file passed using -T option or using #pragma link() */ + CUSTOM("custom"); /** The default target platform. */ public static final TargetPlatform DEFAULT = C64BASIC; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 4490cd32a..cfb3e6e8c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -14,6 +14,7 @@ import dk.camelot64.kickc.model.types.*; import dk.camelot64.kickc.model.values.*; import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos; +import java.io.File; import java.util.*; /** @@ -77,23 +78,15 @@ public class Pass4CodeGeneration { asm.startChunk(currentScope, null, "File Comments"); generateComments(asm, program.getFileComments()); - Number programPc; - if(program.getProgramPc() != null) { - programPc = program.getProgramPc(); - } else { - if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) { - programPc = 0x080d; - } else { - programPc = 0x2000; - } - } - + String outputPrgPath = new File(program.getFileName()).getName()+".prg"; asm.startChunk(currentScope, null, "Upstart"); + Number programPc = program.getProgramPc(); if(TargetPlatform.C64BASIC_SEGMENTS.equals(program.getTargetPlatform())) { useSegments = true; currentCodeSegmentName = "Code"; currentDataSegmentName = "Data"; - asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\"")); + if(programPc==null) programPc = 0x080d; + asm.addLine(new AsmFile(outputPrgPath).param("type", "\"prg\"").param("segments", "\"Program\"")); asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Basic,Code,Data\"")); asm.addLine(new AsmSegmentDef("Basic").param("start", "$0801")); asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc))); @@ -101,22 +94,29 @@ public class Pass4CodeGeneration { asm.addLine(new AsmSegment("Basic")); asm.addLine(new AsmBasicUpstart("bbegin")); setCurrentSegment(currentCodeSegmentName, asm); + } else if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) { + useSegments = false; + if(programPc==null) programPc = 0x080d; + asm.addLine(new AsmSetPc("Basic", AsmFormat.getAsmNumber(0x0801))); + asm.addLine(new AsmBasicUpstart("bbegin")); + asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc))); } else if(TargetPlatform.ASM6502_SEGMENTS.equals(program.getTargetPlatform())) { useSegments = true; currentCodeSegmentName = "Code"; currentDataSegmentName = "Data"; - asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\"")); + if(programPc==null) programPc = 0x2000; + asm.addLine(new AsmFile(outputPrgPath).param("type", "\"prg\"").param("segments", "\"Program\"")); asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Code,Data\"")); asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc))); asm.addLine(new AsmSegmentDef("Data").param("startAfter", "\"Code\"")); setCurrentSegment(currentCodeSegmentName, asm); } else if(TargetPlatform.ASM6502.equals(program.getTargetPlatform())) { useSegments = false; + if(programPc==null) programPc = 0x2000; asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc))); - } else if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) { - useSegments = false; - asm.addLine(new AsmSetPc("Basic", AsmFormat.getAsmNumber(0x0801))); - asm.addLine(new AsmBasicUpstart("bbegin")); + } else if(TargetPlatform.CUSTOM.equals(program.getTargetPlatform())) { + useSegments = true; + if(programPc==null) programPc = 0x2000; asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc))); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 16864a518..001d3d0bb 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -106,6 +106,16 @@ public class TestPrograms { compileAndCompare("platform-asm6502"); } + @Test + public void testPlatformC64BasicSegments() throws IOException, URISyntaxException { + compileAndCompare("platform-c64basic_segments"); + } + + @Test + public void testPlatformAsm6502Segments() throws IOException, URISyntaxException { + compileAndCompare("platform-asm6502_segments"); + } + @Test public void testEuclid2() throws IOException, URISyntaxException { compileAndCompare("euclid-3"); diff --git a/src/test/kc/platform-asm6502_segments.kc b/src/test/kc/platform-asm6502_segments.kc new file mode 100644 index 000000000..1d2e85708 --- /dev/null +++ b/src/test/kc/platform-asm6502_segments.kc @@ -0,0 +1,10 @@ +// Tests the target platform ASM6502_SEGMENTS + +#pragma target(asm6502_segments) + +unsigned char[10] TABLE; + +void main() { + for(char i=0;i<10;i++) + TABLE[i] = i; +} \ No newline at end of file diff --git a/src/test/kc/platform-c64basic_segments.kc b/src/test/kc/platform-c64basic_segments.kc new file mode 100644 index 000000000..c40887062 --- /dev/null +++ b/src/test/kc/platform-c64basic_segments.kc @@ -0,0 +1,10 @@ +// Tests the target platform C64BASIC_SEGMENTS + +#pragma target(c64basic_segments) + +unsigned char[10] TABLE; + +void main() { + for(char i=0;i<10;i++) + TABLE[i] = i; +} \ No newline at end of file diff --git a/src/test/ref/platform-asm6502_segments.asm b/src/test/ref/platform-asm6502_segments.asm new file mode 100644 index 000000000..83efb970b --- /dev/null +++ b/src/test/ref/platform-asm6502_segments.asm @@ -0,0 +1,18 @@ +// Tests the target platform ASM6502_SEGMENTS +.file [ name="platform-asm6502_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Code,Data" ] +.segmentdef Code [ start=$2000 ] +.segmentdef Data [ startAfter="Code" ] +.segment Code +main: { + ldx #0 + b2: + txa + sta TABLE,x + inx + cpx #$a + bcc b2 + rts +} +.segment Data + TABLE: .fill $a, 0 diff --git a/src/test/ref/platform-asm6502_segments.cfg b/src/test/ref/platform-asm6502_segments.cfg new file mode 100644 index 000000000..3b6043e51 --- /dev/null +++ b/src/test/ref/platform-asm6502_segments.cfg @@ -0,0 +1,23 @@ +@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 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main main::@1 + [5] (byte) main::i#4 ← phi( main::@1/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 + [7] (byte) main::i#1 ← ++ (byte) main::i#4 + to:main::@1 +main::@1: scope:[main] from main::@2 + [8] if((byte) main::i#1<(byte) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return + to:@return diff --git a/src/test/ref/platform-asm6502_segments.log b/src/test/ref/platform-asm6502_segments.log new file mode 100644 index 000000000..ccb769179 --- /dev/null +++ b/src/test/ref/platform-asm6502_segments.log @@ -0,0 +1,365 @@ +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte[$a]) TABLE#0 ← { fill( $a, 0) } + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (number) 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[$a]) TABLE#0 + (byte) main::i#3) ← (byte) main::i#3 + (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 +(byte[$a]) TABLE +(byte[$a]) TABLE#0 +(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 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) $a in (bool~) main::$0 ← (byte) main::i#2 < (number) $a +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 0 +Simplifying constant integer cast $a +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $a +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) 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 right-side identified [0] (byte[$a]) TABLE#0 ← { fill( $a, 0) } +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte[$a]) TABLE#0 = { fill( $a, 0) } +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Successful SSA optimization Pass2LoopHeadConstantIdentification +Alias (byte) main::i#1 = (byte) main::i#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) main::i#5 (const byte) main::i#0 +Successful SSA optimization Pass2IdenticalPhiElimination +if() condition always true - replacing block destination [9] if((const byte) main::i#0<(byte) $a) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@7(between main::@1 and main::@2) +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 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1_1 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [12] main::i#6 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@1_1 +Culled Empty Block (label) main::@7 +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 + +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 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main main::@1 + [5] (byte) main::i#4 ← phi( main::@1/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 + [7] (byte) main::i#1 ← ++ (byte) main::i#4 + to:main::@1 +main::@1: scope:[main] from main::@2 + [8] if((byte) main::i#1<(byte) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte[$a]) TABLE +(void()) main() +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#4 22.0 + +Initial phi equivalence classes +[ main::i#4 main::i#1 ] +Complete equivalence classes +[ main::i#4 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#4 main::i#1 ] + +INITIAL ASM +Target platform is asm6502_segments + // File Comments +// Tests the target platform ASM6502_SEGMENTS + // Upstart +.file [ name="platform-asm6502_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Code,Data" ] +.segmentdef Code [ start=$2000 ] +.segmentdef Data [ startAfter="Code" ] +.segment Code + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label i = 2 + // [5] phi from main to main::@2 [phi:main->main::@2] + b2_from_main: + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b2 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + jmp b2 + // main::@2 + b2: + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy.z i + tya + sta TABLE,y + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuz1=_inc_vbuz1 + inc.z i + jmp b1 + // main::@1 + b1: + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$a + bcc b2_from_b1 + jmp breturn + // main::@return + breturn: + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::i#4 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 38.5: zp ZP_BYTE:2 [ main::i#4 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 293 combination reg byte x [ main::i#4 main::i#1 ] +Uplifting [] best 293 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests the target platform ASM6502_SEGMENTS + // Upstart +.file [ name="platform-asm6502_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Code,Data" ] +.segmentdef Code [ start=$2000 ] +.segmentdef Data [ startAfter="Code" ] +.segment Code + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // [5] phi from main to main::@2 [phi:main->main::@2] + b2_from_main: + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + jmp b2 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + jmp b2 + // main::@2 + b2: + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta TABLE,x + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuxx=_inc_vbuxx + inx + jmp b1 + // main::@1 + b1: + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc b2_from_b1 + jmp breturn + // main::@return + breturn: + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b2_from_b1 with b2 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b2_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bbegin: +Removing instruction bend: +Removing instruction b2_from_main: +Removing instruction b1: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b2 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[$a]) TABLE +(const byte[$a]) TABLE#0 TABLE = { fill( $a, 0) } +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#4 reg byte x 22.0 + +reg byte x [ main::i#4 main::i#1 ] + + +FINAL ASSEMBLER +Score: 161 + + // File Comments +// Tests the target platform ASM6502_SEGMENTS + // Upstart +.file [ name="platform-asm6502_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Code,Data" ] +.segmentdef Code [ start=$2000 ] +.segmentdef Data [ startAfter="Code" ] +.segment Code + // Global Constants & labels + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // [5] phi from main to main::@2 [phi:main->main::@2] + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + // main::@2 + b2: + // TABLE[i] = i + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta TABLE,x + // for(char i=0;i<10;i++) + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuxx=_inc_vbuxx + inx + // main::@1 + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc b2 + // main::@return + // } + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + diff --git a/src/test/ref/platform-asm6502_segments.sym b/src/test/ref/platform-asm6502_segments.sym new file mode 100644 index 000000000..20adadfeb --- /dev/null +++ b/src/test/ref/platform-asm6502_segments.sym @@ -0,0 +1,14 @@ +(label) @1 +(label) @begin +(label) @end +(byte[$a]) TABLE +(const byte[$a]) TABLE#0 TABLE = { fill( $a, 0) } +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#4 reg byte x 22.0 + +reg byte x [ main::i#4 main::i#1 ] diff --git a/src/test/ref/platform-c64basic_segments.asm b/src/test/ref/platform-c64basic_segments.asm new file mode 100644 index 000000000..2c8e358c0 --- /dev/null +++ b/src/test/ref/platform-c64basic_segments.asm @@ -0,0 +1,21 @@ +// Tests the target platform C64BASIC_SEGMENTS +.file [ name="platform-c64basic_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Basic,Code,Data" ] +.segmentdef Basic [ start=$0801 ] +.segmentdef Code [ start=$80d ] +.segmentdef Data [ startAfter="Code" ] +.segment Basic +:BasicUpstart(main) +.segment Code +main: { + ldx #0 + b2: + txa + sta TABLE,x + inx + cpx #$a + bcc b2 + rts +} +.segment Data + TABLE: .fill $a, 0 diff --git a/src/test/ref/platform-c64basic_segments.cfg b/src/test/ref/platform-c64basic_segments.cfg new file mode 100644 index 000000000..3b6043e51 --- /dev/null +++ b/src/test/ref/platform-c64basic_segments.cfg @@ -0,0 +1,23 @@ +@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 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main main::@1 + [5] (byte) main::i#4 ← phi( main::@1/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 + [7] (byte) main::i#1 ← ++ (byte) main::i#4 + to:main::@1 +main::@1: scope:[main] from main::@2 + [8] if((byte) main::i#1<(byte) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return + to:@return diff --git a/src/test/ref/platform-c64basic_segments.log b/src/test/ref/platform-c64basic_segments.log new file mode 100644 index 000000000..66299813a --- /dev/null +++ b/src/test/ref/platform-c64basic_segments.log @@ -0,0 +1,376 @@ +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte[$a]) TABLE#0 ← { fill( $a, 0) } + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (number) 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[$a]) TABLE#0 + (byte) main::i#3) ← (byte) main::i#3 + (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 +(byte[$a]) TABLE +(byte[$a]) TABLE#0 +(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 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) $a in (bool~) main::$0 ← (byte) main::i#2 < (number) $a +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 0 +Simplifying constant integer cast $a +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $a +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) 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 right-side identified [0] (byte[$a]) TABLE#0 ← { fill( $a, 0) } +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte[$a]) TABLE#0 = { fill( $a, 0) } +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Successful SSA optimization Pass2LoopHeadConstantIdentification +Alias (byte) main::i#1 = (byte) main::i#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) main::i#5 (const byte) main::i#0 +Successful SSA optimization Pass2IdenticalPhiElimination +if() condition always true - replacing block destination [9] if((const byte) main::i#0<(byte) $a) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@7(between main::@1 and main::@2) +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 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1_1 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [12] main::i#6 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@1_1 +Culled Empty Block (label) main::@7 +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 + +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 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main main::@1 + [5] (byte) main::i#4 ← phi( main::@1/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 + [7] (byte) main::i#1 ← ++ (byte) main::i#4 + to:main::@1 +main::@1: scope:[main] from main::@2 + [8] if((byte) main::i#1<(byte) $a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte[$a]) TABLE +(void()) main() +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#4 22.0 + +Initial phi equivalence classes +[ main::i#4 main::i#1 ] +Complete equivalence classes +[ main::i#4 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#4 main::i#1 ] + +INITIAL ASM +Target platform is c64basic_segments + // File Comments +// Tests the target platform C64BASIC_SEGMENTS + // Upstart +.file [ name="platform-c64basic_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Basic,Code,Data" ] +.segmentdef Basic [ start=$0801 ] +.segmentdef Code [ start=$80d ] +.segmentdef Data [ startAfter="Code" ] +.segment Basic +:BasicUpstart(bbegin) +.segment Code + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label i = 2 + // [5] phi from main to main::@2 [phi:main->main::@2] + b2_from_main: + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b2 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + jmp b2 + // main::@2 + b2: + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy.z i + tya + sta TABLE,y + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuz1=_inc_vbuz1 + inc.z i + jmp b1 + // main::@1 + b1: + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$a + bcc b2_from_b1 + jmp breturn + // main::@return + breturn: + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::i#4 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 38.5: zp ZP_BYTE:2 [ main::i#4 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 293 combination reg byte x [ main::i#4 main::i#1 ] +Uplifting [] best 293 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests the target platform C64BASIC_SEGMENTS + // Upstart +.file [ name="platform-c64basic_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Basic,Code,Data" ] +.segmentdef Basic [ start=$0801 ] +.segmentdef Code [ start=$80d ] +.segmentdef Data [ startAfter="Code" ] +.segment Basic +:BasicUpstart(bbegin) +.segment Code + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // [5] phi from main to main::@2 [phi:main->main::@2] + b2_from_main: + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + jmp b2 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + jmp b2 + // main::@2 + b2: + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta TABLE,x + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuxx=_inc_vbuxx + inx + jmp b1 + // main::@1 + b1: + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc b2_from_b1 + jmp breturn + // main::@return + breturn: + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b2_from_b1 with b2 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b2_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b2_from_main: +Removing instruction b1: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b2 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[$a]) TABLE +(const byte[$a]) TABLE#0 TABLE = { fill( $a, 0) } +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#4 reg byte x 22.0 + +reg byte x [ main::i#4 main::i#1 ] + + +FINAL ASSEMBLER +Score: 161 + + // File Comments +// Tests the target platform C64BASIC_SEGMENTS + // Upstart +.file [ name="platform-c64basic_segments.prg",type="prg",segments="Program" ] +.segmentdef Program [ segments="Basic,Code,Data" ] +.segmentdef Basic [ start=$0801 ] +.segmentdef Code [ start=$80d ] +.segmentdef Data [ startAfter="Code" ] +.segment Basic +:BasicUpstart(main) +.segment Code + // Global Constants & labels + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // [5] phi from main to main::@2 [phi:main->main::@2] + // [5] phi (byte) main::i#4 = (byte) 0 [phi:main->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // [5] phi (byte) main::i#4 = (byte) main::i#1 [phi:main::@1->main::@2#0] -- register_copy + // main::@2 + b2: + // TABLE[i] = i + // [6] *((const byte[$a]) TABLE#0 + (byte) main::i#4) ← (byte) main::i#4 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta TABLE,x + // for(char i=0;i<10;i++) + // [7] (byte) main::i#1 ← ++ (byte) main::i#4 -- vbuxx=_inc_vbuxx + inx + // main::@1 + // [8] if((byte) main::i#1<(byte) $a) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$a + bcc b2 + // main::@return + // } + // [9] return + rts +} + // File Data +.segment Data + TABLE: .fill $a, 0 + diff --git a/src/test/ref/platform-c64basic_segments.sym b/src/test/ref/platform-c64basic_segments.sym new file mode 100644 index 000000000..20adadfeb --- /dev/null +++ b/src/test/ref/platform-c64basic_segments.sym @@ -0,0 +1,14 @@ +(label) @1 +(label) @begin +(label) @end +(byte[$a]) TABLE +(const byte[$a]) TABLE#0 TABLE = { fill( $a, 0) } +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#4 reg byte x 22.0 + +reg byte x [ main::i#4 main::i#1 ] diff --git a/src/test/script/deploy-local.sh b/src/test/script/deploy-local.sh index 591bd718b..b13fedc17 100755 --- a/src/test/script/deploy-local.sh +++ b/src/test/script/deploy-local.sh @@ -7,3 +7,4 @@ popd rm -r ${C64_HOME}/kickc_local/* unzip -d ${C64_HOME}/kickc_local ${C64_HOME}/kickc/target/kickc-release.zip mv ${C64_HOME}/kickc_local/kickc/* ${C64_HOME}/kickc_local/ +rmdir ${C64_HOME}/kickc_local/kickc \ No newline at end of file