diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java b/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java index f24ec9e8b..67f5f2844 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java @@ -34,8 +34,12 @@ public class AsmFormat { return getAsmBool(((ConstantBool) value).getBool()); } else if(value instanceof ConstantChar) { ConstantChar constantChar = (ConstantChar) value; - String escapedChar = constantChar.getCharEscaped(); - return "'" + escapedChar + "'"; + if(!constantChar.getEncoding().hasEncoding(constantChar.getChar())) + return getAsmNumber(constantChar.getInteger()); + else { + String escapedChar = constantChar.getCharEscaped(); + return "'" + escapedChar + "'"; + } } else if(value instanceof ConstantString) { String stringValue = ((ConstantString) value).getValue(); String escapedString = ((ConstantString) value).getStringEscaped(); diff --git a/src/main/java/dk/camelot64/kickc/model/values/StringEncoding.java b/src/main/java/dk/camelot64/kickc/model/values/StringEncoding.java index 07461a284..96b9e0b05 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/StringEncoding.java +++ b/src/main/java/dk/camelot64/kickc/model/values/StringEncoding.java @@ -14,6 +14,14 @@ public enum StringEncoding { SCREENCODE_MIXED("screencode_mixed", "sm", CharToPetsciiConverter.charToScreenCode_mixed), SCREENCODE_UPPER("screencode_upper", "su", CharToPetsciiConverter.charToScreenCode_upper); + + /** Char value used to encode \xnn chars without a value within the chosen encoding. A char C is encoded as CHAR_SPECIAL_VAL+C */ + public static final char CHAR_SPECIAL_VAL = 64000; + /** The minimal value of a specially encoded char. */ + public static final char CHAR_SPECIAL_MIN = CHAR_SPECIAL_VAL + Byte.MIN_VALUE; + /** The maximal value of a specially encoded char. */ + public static final char CHAR_SPECIAL_MAX = CHAR_SPECIAL_VAL + Byte.MAX_VALUE; + /** The encoding name. */ public final String name; @@ -72,9 +80,24 @@ public enum StringEncoding { */ public Long encodedFromChar(Character aChar) { Byte encodedValue = mapping.get(aChar); + if(encodedValue != null) return encodedValue.longValue(); + else + // Char is not in encoding - it must be made up! + return (long) aChar - CHAR_SPECIAL_VAL; } + /** + * Determine if a character has en encoding within the specific encoding + * @param aChar The char to examine + * @return true if the char has a proper encoding. False if it does not. + */ + public boolean hasEncoding(Character aChar) { + Byte encodedValue = mapping.get(aChar); + return encodedValue != null; + } + + /** * Get UNICODE/ASCII character for a specific encoded integer value using the specific encoding * @@ -86,7 +109,8 @@ public enum StringEncoding { if(mapEntry.getValue() == encodedValue.byteValue()) return mapEntry.getKey(); } - return null; + // If the mapping does not handle the Char - make one up + return (char) (CHAR_SPECIAL_VAL + encodedValue); } /** @@ -171,9 +195,12 @@ public enum StringEncoding { return Character.toString(aChar); case '\\': return "\\\\"; - default: - return Character.toString(aChar); } + if(aChar >= CHAR_SPECIAL_MIN && aChar <= CHAR_SPECIAL_MAX) { + final byte charValue = (byte) (aChar - CHAR_SPECIAL_VAL); + return String.format("\\$%x", charValue); + } else + return Character.toString(aChar); } /** @@ -188,5 +215,4 @@ public enum StringEncoding { return escaped.toString(); } - } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index e916ced33..f165adefe 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -641,11 +641,6 @@ public class TestPrograms { compileAndCompare("code-after-return"); } - @Test - public void testStringEscapesErr2() throws IOException, URISyntaxException { - assertError("string-escapes-err-2", "No character 0xff in encoding petscii_mixed"); - } - @Test public void testStringEscapesErr1() throws IOException, URISyntaxException { assertError("string-escapes-err-1", "Illegal string escape sequence"); @@ -656,6 +651,11 @@ public class TestPrograms { assertError("string-escapes-err-0", "Unfinished string escape sequence at end of string"); } + @Test + public void testStringEscapes5() throws IOException, URISyntaxException { + compileAndCompare("string-escapes-5"); + } + @Test public void testStringEscapes4() throws IOException, URISyntaxException { compileAndCompare("string-escapes-4"); diff --git a/src/test/kc/string-escapes-3.kc b/src/test/kc/string-escapes-3.kc index ed8e23885..5824e202d 100644 --- a/src/test/kc/string-escapes-3.kc +++ b/src/test/kc/string-escapes-3.kc @@ -4,6 +4,8 @@ #pragma encoding(petscii_mixed) char MESSAGE[] = "hello\nworld"; +char CH = '\n'; + char* SCREEN = 0x0400; void main() { @@ -22,5 +24,7 @@ void main() { } msg++; } + + SCREEN[0x50] = CH; } \ No newline at end of file diff --git a/src/test/kc/string-escapes-5.kc b/src/test/kc/string-escapes-5.kc new file mode 100644 index 000000000..c4b6300e5 --- /dev/null +++ b/src/test/kc/string-escapes-5.kc @@ -0,0 +1,16 @@ +// Test using some simple supported string escape +// Uses \xnn to add chars by hex-code that do not exist with the encoding. + +#pragma encoding(petscii_mixed) +char MESSAGE[] = "qwe\xffasd\xferty"; + +char CH = '\xff'; + +char * const SCREEN = 0x0400; + +void main() { + char i=0; + while(MESSAGE[i]) + SCREEN[i] = MESSAGE[i++]; + SCREEN[0x28] = CH; +} \ No newline at end of file diff --git a/src/test/kc/string-escapes-err-2.kc b/src/test/kc/string-escapes-err-2.kc deleted file mode 100644 index 95731a594..000000000 --- a/src/test/kc/string-escapes-err-2.kc +++ /dev/null @@ -1,8 +0,0 @@ -// Test errors using string escape sequences -// Unsupported hex character sequence - -#pragma encoding(petscii_mixed) -char MESSAGE[] = "qwe\xff"; - -void main() { -} \ No newline at end of file diff --git a/src/test/ref/string-escapes-3.asm b/src/test/ref/string-escapes-3.asm index 775588bc1..f5f8e5a2c 100644 --- a/src/test/ref/string-escapes-3.asm +++ b/src/test/ref/string-escapes-3.asm @@ -3,6 +3,9 @@ .pc = $801 "Basic" :BasicUpstart(main) .pc = $80d "Program" +.encoding "petscii_mixed" + .const CH = '\n' + .label SCREEN = $400 main: { .label cursor = 6 .label msg = 2 @@ -25,6 +28,9 @@ main: { lda (msg),y cmp #0 bne __b2 + // SCREEN[0x50] = CH + lda #CH + sta SCREEN+$50 // } rts __b2: @@ -32,11 +38,10 @@ main: { // line += 0x28; // cursor = line; // break; - .encoding "petscii_mixed" lda #'\n' ldy #0 cmp (msg),y - beq __b3 + beq __b4 // *msg & 0x3f lda #$3f and (msg),y @@ -47,14 +52,14 @@ main: { bne !+ inc.z cursor+1 !: - __b5: + __b6: // msg++; inc.z msg bne !+ inc.z msg+1 !: jmp __b1 - __b3: + __b4: // line += 0x28 lda #$28 clc @@ -67,7 +72,7 @@ main: { sta.z line lda.z cursor+1 sta.z line+1 - jmp __b5 + jmp __b6 } MESSAGE: .text @"hello\nworld" .byte 0 diff --git a/src/test/ref/string-escapes-3.cfg b/src/test/ref/string-escapes-3.cfg index dbd1f4bdd..828031a64 100644 --- a/src/test/ref/string-escapes-3.cfg +++ b/src/test/ref/string-escapes-3.cfg @@ -12,30 +12,33 @@ main: scope:[main] from @1 [4] phi() to:main::@1 -main::@1: scope:[main] from main main::@5 - [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 ) - [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 ) - [5] (byte*) main::msg#2 ← phi( main/(const byte*) MESSAGE main::@5/(byte*) main::msg#1 ) +main::@1: scope:[main] from main main::@6 + [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@6/(byte*) main::cursor#6 ) + [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@6/(byte*) main::line#5 ) + [5] (byte*) main::msg#2 ← phi( main/(const byte*) MESSAGE main::@6/(byte*) main::msg#1 ) [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH to:main::@return -main::@return: scope:[main] from main::@1 - [7] return +main::@return: scope:[main] from main::@3 + [8] return to:@return main::@2: scope:[main] from main::@1 - [8] if(*((byte*) main::msg#2)==(byte) ' -'pm) goto main::@3 - to:main::@4 -main::@4: scope:[main] from main::@2 - [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f - [10] *((byte*) main::cursor#3) ← (byte~) main::$0 - [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + [9] if(*((byte*) main::msg#2)==(byte) ' +'pm) goto main::@4 to:main::@5 -main::@5: scope:[main] from main::@3 main::@4 - [12] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 ) - [12] (byte*) main::line#5 ← phi( main::@3/(byte*) main::line#8 main::@4/(byte*) main::line#2 ) - [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 +main::@5: scope:[main] from main::@2 + [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f + [11] *((byte*) main::cursor#3) ← (byte~) main::$0 + [12] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + to:main::@6 +main::@6: scope:[main] from main::@4 main::@5 + [13] (byte*) main::cursor#6 ← phi( main::@4/(byte*) main::cursor#1 main::@5/(byte*) main::cursor#2 ) + [13] (byte*) main::line#5 ← phi( main::@4/(byte*) main::line#8 main::@5/(byte*) main::line#2 ) + [14] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 to:main::@1 -main::@3: scope:[main] from main::@2 - [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 - [15] (byte*) main::line#8 ← (byte*) main::cursor#1 - to:main::@5 +main::@4: scope:[main] from main::@2 + [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 + [16] (byte*) main::line#8 ← (byte*) main::cursor#1 + to:main::@6 diff --git a/src/test/ref/string-escapes-3.log b/src/test/ref/string-escapes-3.log index ba3bd7e9d..aa7ab9614 100644 --- a/src/test/ref/string-escapes-3.log +++ b/src/test/ref/string-escapes-3.log @@ -1,7 +1,7 @@ Warning! Adding boolean cast to non-boolean condition *((byte*) main::msg) +Identified constant variable (byte) CH Identified constant variable (byte*) SCREEN Culled Empty Block (label) main::@9 -Culled Empty Block (label) main::@3 Culled Empty Block (label) main::@10 Culled Empty Block (label) main::@11 Culled Empty Block (label) main::@12 @@ -24,7 +24,7 @@ main::@1: scope:[main] from main main::@6 (byte*) main::msg#2 ← phi( main/(byte*) main::msg#0 main::@6/(byte*) main::msg#1 ) (bool~) main::$1 ← (number) 0 != *((byte*) main::msg#2) if((bool~) main::$1) goto main::@2 - to:main::@return + to:main::@3 main::@2: scope:[main] from main::@1 (byte*) main::cursor#4 ← phi( main::@1/(byte*) main::cursor#5 ) (byte*) main::line#3 ← phi( main::@1/(byte*) main::line#4 ) @@ -32,6 +32,9 @@ main::@2: scope:[main] from main::@1 if(*((byte*) main::msg#3)==(byte) ' 'pm) goto main::@4 to:main::@5 +main::@3: scope:[main] from main::@1 + *((const byte*) SCREEN + (number) $50) ← (const byte) CH + to:main::@return main::@4: scope:[main] from main::@2 (byte*) main::msg#6 ← phi( main::@2/(byte*) main::msg#3 ) (byte*) main::line#2 ← phi( main::@2/(byte*) main::line#3 ) @@ -52,7 +55,7 @@ main::@6: scope:[main] from main::@4 main::@5 (byte*) main::msg#5 ← phi( main::@4/(byte*) main::msg#6 main::@5/(byte*) main::msg#4 ) (byte*) main::msg#1 ← ++ (byte*) main::msg#5 to:main::@1 -main::@return: scope:[main] from main::@1 +main::@return: scope:[main] from main::@3 return to:@return @1: scope:[] from @begin @@ -67,13 +70,17 @@ SYMBOL TABLE SSA (label) @2 (label) @begin (label) @end +(const byte) CH = (byte) ' +'pm (const byte*) MESSAGE[] = (byte*) "hello world"pm +(const byte*) SCREEN = (byte*)(number) $400 (void()) main() (number~) main::$0 (bool~) main::$1 (label) main::@1 (label) main::@2 +(label) main::@3 (label) main::@4 (label) main::@5 (label) main::@6 @@ -104,16 +111,20 @@ world"pm (byte*) main::msg#6 Adding number conversion cast (unumber) 0 in (bool~) main::$1 ← (number) 0 != *((byte*) main::msg#2) +Adding number conversion cast (unumber) $50 in *((const byte*) SCREEN + (number) $50) ← (const byte) CH Adding number conversion cast (unumber) $28 in (byte*) main::line#1 ← (byte*) main::line#2 + (number) $28 Adding number conversion cast (unumber) $3f in (number~) main::$0 ← *((byte*) main::msg#4) & (number) $3f Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← *((byte*) main::msg#4) & (unumber)(number) $3f Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant pointer cast (byte*) 1024 +Simplifying constant pointer cast (byte*) 1024 Simplifying constant integer cast 0 +Simplifying constant integer cast $50 Simplifying constant integer cast $28 Simplifying constant integer cast $3f Successful SSA optimization PassNCastSimplification Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $50 Finalized unsigned number type (byte) $28 Finalized unsigned number type (byte) $3f Successful SSA optimization PassNFinalizeNumberTypeConversions @@ -136,6 +147,8 @@ Inlining constant with var siblings (const byte*) main::msg#0 Constant inlined main::cursor#0 = (byte*) 1024 Constant inlined main::msg#0 = (const byte*) MESSAGE Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+$50) +Successful SSA optimization Pass2ConstantAdditionElimination Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 @@ -145,18 +158,15 @@ CALL GRAPH Calls in [] to main:2 Created 5 initial phi equivalence classes -Coalesced [13] main::line#9 ← main::line#2 -Coalesced [14] main::cursor#9 ← main::cursor#2 -Coalesced [17] main::msg#7 ← main::msg#1 -Coalesced (already) [18] main::line#7 ← main::line#5 -Coalesced [19] main::cursor#7 ← main::cursor#6 -Not coalescing [21] main::line#8 ← main::cursor#1 -Coalesced [22] main::cursor#8 ← main::cursor#1 +Coalesced [14] main::line#9 ← main::line#2 +Coalesced [15] main::cursor#9 ← main::cursor#2 +Coalesced [18] main::msg#7 ← main::msg#1 +Coalesced (already) [19] main::line#7 ← main::line#5 +Coalesced [20] main::cursor#7 ← main::cursor#6 +Not coalescing [22] main::line#8 ← main::cursor#1 +Coalesced [23] main::cursor#8 ← main::cursor#1 Coalesced down to 3 phi equivalence classes Culled Empty Block (label) @2 -Renumbering block main::@4 to main::@3 -Renumbering block main::@5 to main::@4 -Renumbering block main::@6 to main::@5 Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @end @@ -177,33 +187,36 @@ FINAL CONTROL FLOW GRAPH main: scope:[main] from @1 [4] phi() to:main::@1 -main::@1: scope:[main] from main main::@5 - [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 ) - [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 ) - [5] (byte*) main::msg#2 ← phi( main/(const byte*) MESSAGE main::@5/(byte*) main::msg#1 ) +main::@1: scope:[main] from main main::@6 + [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@6/(byte*) main::cursor#6 ) + [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@6/(byte*) main::line#5 ) + [5] (byte*) main::msg#2 ← phi( main/(const byte*) MESSAGE main::@6/(byte*) main::msg#1 ) [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH to:main::@return -main::@return: scope:[main] from main::@1 - [7] return +main::@return: scope:[main] from main::@3 + [8] return to:@return main::@2: scope:[main] from main::@1 - [8] if(*((byte*) main::msg#2)==(byte) ' -'pm) goto main::@3 - to:main::@4 -main::@4: scope:[main] from main::@2 - [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f - [10] *((byte*) main::cursor#3) ← (byte~) main::$0 - [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + [9] if(*((byte*) main::msg#2)==(byte) ' +'pm) goto main::@4 to:main::@5 -main::@5: scope:[main] from main::@3 main::@4 - [12] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 ) - [12] (byte*) main::line#5 ← phi( main::@3/(byte*) main::line#8 main::@4/(byte*) main::line#2 ) - [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 +main::@5: scope:[main] from main::@2 + [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f + [11] *((byte*) main::cursor#3) ← (byte~) main::$0 + [12] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + to:main::@6 +main::@6: scope:[main] from main::@4 main::@5 + [13] (byte*) main::cursor#6 ← phi( main::@4/(byte*) main::cursor#1 main::@5/(byte*) main::cursor#2 ) + [13] (byte*) main::line#5 ← phi( main::@4/(byte*) main::line#8 main::@5/(byte*) main::line#2 ) + [14] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 to:main::@1 -main::@3: scope:[main] from main::@2 - [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 - [15] (byte*) main::line#8 ← (byte*) main::cursor#1 - to:main::@5 +main::@4: scope:[main] from main::@2 + [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 + [16] (byte*) main::line#8 ← (byte*) main::cursor#1 + to:main::@6 VARIABLE REGISTER WEIGHTS @@ -247,6 +260,9 @@ Target platform is c64basic / MOS6502X :BasicUpstart(__bbegin) .pc = $80d "Program" // Global Constants & labels +.encoding "petscii_mixed" + .const CH = '\n' + .label SCREEN = $400 // @begin __bbegin: // [1] phi from @begin to @1 [phi:@begin->@1] @@ -294,58 +310,63 @@ main: { lda (msg),y cmp #0 bne __b2 + jmp __b3 + // main::@3 + __b3: + // [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$50 jmp __breturn // main::@return __breturn: - // [7] return + // [8] return rts // main::@2 __b2: - // [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 - .encoding "petscii_mixed" + // [9] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@4 -- _deref_pbuz1_eq_vbuc1_then_la1 lda #'\n' ldy #0 cmp (msg),y - beq __b3 - jmp __b4 - // main::@4 - __b4: - // [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuz1=_deref_pbuz2_band_vbuc1 + beq __b4 + jmp __b5 + // main::@5 + __b5: + // [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuz1=_deref_pbuz2_band_vbuc1 lda #$3f ldy #0 and (msg),y sta.z __0 - // [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuz2 + // [11] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuz2 lda.z __0 ldy #0 sta (cursor),y - // [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + // [12] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 inc.z cursor bne !+ inc.z cursor+1 !: - // [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] - __b5_from___b3: - __b5_from___b4: - // [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy - // [12] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy - jmp __b5 - // main::@5 - __b5: - // [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + // [13] phi from main::@4 main::@5 to main::@6 [phi:main::@4/main::@5->main::@6] + __b6_from___b4: + __b6_from___b5: + // [13] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@4/main::@5->main::@6#0] -- register_copy + // [13] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@4/main::@5->main::@6#1] -- register_copy + jmp __b6 + // main::@6 + __b6: + // [14] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 inc.z msg bne !+ inc.z msg+1 !: - // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] - __b1_from___b5: - // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy - // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy - // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + // [5] phi from main::@6 to main::@1 [phi:main::@6->main::@1] + __b1_from___b6: + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@6->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@6->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@6->main::@1#2] -- register_copy jmp __b1 - // main::@3 - __b3: - // [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + // main::@4 + __b4: + // [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 lda #$28 clc adc.z line @@ -353,12 +374,12 @@ main: { lda #0 adc.z line+1 sta.z cursor+1 - // [15] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + // [16] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 lda.z cursor sta.z line lda.z cursor+1 sta.z line+1 - jmp __b5_from___b3 + jmp __b6_from___b4 } // File Data MESSAGE: .text @"hello\nworld" @@ -366,12 +387,13 @@ main: { REGISTER UPLIFT POTENTIAL REGISTERS Statement [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] { } ) always clobbers reg byte a reg byte y -Statement [8] if(*((byte*) main::msg#2)==(byte) ' -'pm) goto main::@3 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] { } ) always clobbers reg byte a reg byte y -Statement [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] { } ) always clobbers reg byte a reg byte y -Statement [10] *((byte*) main::cursor#3) ← (byte~) main::$0 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] { } ) always clobbers reg byte y -Statement [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 [ main::msg#2 main::cursor#1 ] ( main:2 [ main::msg#2 main::cursor#1 ] { } ) always clobbers reg byte a -Statement [15] (byte*) main::line#8 ← (byte*) main::cursor#1 [ main::msg#2 main::line#8 main::cursor#1 ] ( main:2 [ main::msg#2 main::line#8 main::cursor#1 ] { } ) always clobbers reg byte a +Statement [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH [ ] ( main:2 [ ] { } ) always clobbers reg byte a +Statement [9] if(*((byte*) main::msg#2)==(byte) ' +'pm) goto main::@4 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] { } ) always clobbers reg byte a reg byte y +Statement [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] { } ) always clobbers reg byte a reg byte y +Statement [11] *((byte*) main::cursor#3) ← (byte~) main::$0 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] { } ) always clobbers reg byte y +Statement [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 [ main::msg#2 main::cursor#1 ] ( main:2 [ main::msg#2 main::cursor#1 ] { } ) always clobbers reg byte a +Statement [16] (byte*) main::line#8 ← (byte*) main::cursor#1 [ main::msg#2 main::line#8 main::cursor#1 ] ( main:2 [ main::msg#2 main::line#8 main::cursor#1 ] { } ) always clobbers reg byte a Potential registers zp[2]:2 [ main::msg#2 main::msg#1 ] : zp[2]:2 , Potential registers zp[2]:4 [ main::line#2 main::line#5 main::line#8 ] : zp[2]:4 , Potential registers zp[2]:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] : zp[2]:6 , @@ -381,8 +403,8 @@ REGISTER UPLIFT SCOPES Uplift Scope [main] 565.6: zp[2]:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] 404: zp[2]:4 [ main::line#2 main::line#5 main::line#8 ] 258.11: zp[2]:2 [ main::msg#2 main::msg#1 ] 202: zp[1]:8 [ main::$0 ] Uplift Scope [] -Uplifting [main] best 1463 combination zp[2]:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] zp[2]:4 [ main::line#2 main::line#5 main::line#8 ] zp[2]:2 [ main::msg#2 main::msg#1 ] reg byte a [ main::$0 ] -Uplifting [] best 1463 combination +Uplifting [main] best 1472 combination zp[2]:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] zp[2]:4 [ main::line#2 main::line#5 main::line#8 ] zp[2]:2 [ main::msg#2 main::msg#1 ] reg byte a [ main::$0 ] +Uplifting [] best 1472 combination ASSEMBLER BEFORE OPTIMIZATION // File Comments @@ -393,6 +415,9 @@ ASSEMBLER BEFORE OPTIMIZATION :BasicUpstart(__bbegin) .pc = $80d "Program" // Global Constants & labels +.encoding "petscii_mixed" + .const CH = '\n' + .label SCREEN = $400 // @begin __bbegin: // [1] phi from @begin to @1 [phi:@begin->@1] @@ -439,56 +464,61 @@ main: { lda (msg),y cmp #0 bne __b2 + jmp __b3 + // main::@3 + __b3: + // [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$50 jmp __breturn // main::@return __breturn: - // [7] return + // [8] return rts // main::@2 __b2: - // [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 - .encoding "petscii_mixed" + // [9] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@4 -- _deref_pbuz1_eq_vbuc1_then_la1 lda #'\n' ldy #0 cmp (msg),y - beq __b3 - jmp __b4 - // main::@4 - __b4: - // [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1 + beq __b4 + jmp __b5 + // main::@5 + __b5: + // [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1 lda #$3f ldy #0 and (msg),y - // [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa + // [11] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa ldy #0 sta (cursor),y - // [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + // [12] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 inc.z cursor bne !+ inc.z cursor+1 !: - // [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] - __b5_from___b3: - __b5_from___b4: - // [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy - // [12] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy - jmp __b5 - // main::@5 - __b5: - // [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + // [13] phi from main::@4 main::@5 to main::@6 [phi:main::@4/main::@5->main::@6] + __b6_from___b4: + __b6_from___b5: + // [13] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@4/main::@5->main::@6#0] -- register_copy + // [13] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@4/main::@5->main::@6#1] -- register_copy + jmp __b6 + // main::@6 + __b6: + // [14] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 inc.z msg bne !+ inc.z msg+1 !: - // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] - __b1_from___b5: - // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy - // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy - // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + // [5] phi from main::@6 to main::@1 [phi:main::@6->main::@1] + __b1_from___b6: + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@6->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@6->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@6->main::@1#2] -- register_copy jmp __b1 - // main::@3 - __b3: - // [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + // main::@4 + __b4: + // [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 lda #$28 clc adc.z line @@ -496,12 +526,12 @@ main: { lda #0 adc.z line+1 sta.z cursor+1 - // [15] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + // [16] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 lda.z cursor sta.z line lda.z cursor+1 sta.z line+1 - jmp __b5_from___b3 + jmp __b6_from___b4 } // File Data MESSAGE: .text @"hello\nworld" @@ -511,25 +541,27 @@ ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __bend Removing instruction jmp __b1 +Removing instruction jmp __b3 Removing instruction jmp __breturn -Removing instruction jmp __b4 Removing instruction jmp __b5 +Removing instruction jmp __b6 Succesful ASM optimization Pass5NextJumpElimination Removing instruction ldy #0 Succesful ASM optimization Pass5UnnecesaryLoadElimination -Replacing label __b5_from___b3 with __b5 +Replacing label __b6_from___b4 with __b6 Removing instruction __b1_from___bbegin: Removing instruction __b1: Removing instruction main_from___b1: Removing instruction __bend_from___b1: -Removing instruction __b5_from___b3: -Removing instruction __b5_from___b4: +Removing instruction __b6_from___b4: +Removing instruction __b6_from___b5: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __bend: Removing instruction __b1_from_main: +Removing instruction __b3: Removing instruction __breturn: -Removing instruction __b4: -Removing instruction __b1_from___b5: +Removing instruction __b5: +Removing instruction __b1_from___b6: Succesful ASM optimization Pass5UnusedLabelElimination Updating BasicUpstart to call main directly Removing instruction jsr main @@ -543,8 +575,11 @@ FINAL SYMBOL TABLE (label) @1 (label) @begin (label) @end +(const byte) CH = (byte) ' +'pm (const byte*) MESSAGE[] = (byte*) "hello world"pm +(const byte*) SCREEN = (byte*) 1024 (void()) main() (byte~) main::$0 reg byte a 202.0 (label) main::@1 @@ -552,6 +587,7 @@ world"pm (label) main::@3 (label) main::@4 (label) main::@5 +(label) main::@6 (label) main::@return (byte*) main::cursor (byte*) main::cursor#1 cursor zp[2]:6 151.5 @@ -573,7 +609,7 @@ reg byte a [ main::$0 ] FINAL ASSEMBLER -Score: 1291 +Score: 1297 // File Comments // Test using some simple supported string escape \n in both string and char @@ -583,6 +619,9 @@ Score: 1291 :BasicUpstart(main) .pc = $80d "Program" // Global Constants & labels +.encoding "petscii_mixed" + .const CH = '\n' + .label SCREEN = $400 // @begin // [1] phi from @begin to @1 [phi:@begin->@1] // @1 @@ -619,9 +658,14 @@ main: { lda (msg),y cmp #0 bne __b2 + // main::@3 + // SCREEN[0x50] = CH + // [7] *((const byte*) SCREEN+(byte) $50) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$50 // main::@return // } - // [7] return + // [8] return rts // main::@2 __b2: @@ -629,46 +673,45 @@ main: { // line += 0x28; // cursor = line; // break; - // [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 - .encoding "petscii_mixed" + // [9] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@4 -- _deref_pbuz1_eq_vbuc1_then_la1 lda #'\n' ldy #0 cmp (msg),y - beq __b3 - // main::@4 + beq __b4 + // main::@5 // *msg & 0x3f - // [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1 + // [10] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1 lda #$3f and (msg),y // *cursor++ = *msg & 0x3f - // [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa + // [11] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa sta (cursor),y // *cursor++ = *msg & 0x3f; - // [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + // [12] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 inc.z cursor bne !+ inc.z cursor+1 !: - // [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] - // [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy - // [12] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy - // main::@5 - __b5: + // [13] phi from main::@4 main::@5 to main::@6 [phi:main::@4/main::@5->main::@6] + // [13] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@4/main::@5->main::@6#0] -- register_copy + // [13] phi (byte*) main::line#5 = (byte*) main::line#8 [phi:main::@4/main::@5->main::@6#1] -- register_copy + // main::@6 + __b6: // msg++; - // [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + // [14] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 inc.z msg bne !+ inc.z msg+1 !: - // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] - // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy - // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy - // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + // [5] phi from main::@6 to main::@1 [phi:main::@6->main::@1] + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@6->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@6->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@6->main::@1#2] -- register_copy jmp __b1 - // main::@3 - __b3: + // main::@4 + __b4: // line += 0x28 - // [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + // [15] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 lda #$28 clc adc.z line @@ -676,12 +719,12 @@ main: { lda #0 adc.z line+1 sta.z cursor+1 - // [15] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + // [16] (byte*) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 lda.z cursor sta.z line lda.z cursor+1 sta.z line+1 - jmp __b5 + jmp __b6 } // File Data MESSAGE: .text @"hello\nworld" diff --git a/src/test/ref/string-escapes-3.sym b/src/test/ref/string-escapes-3.sym index b0c1f5270..7a5dc7920 100644 --- a/src/test/ref/string-escapes-3.sym +++ b/src/test/ref/string-escapes-3.sym @@ -1,8 +1,11 @@ (label) @1 (label) @begin (label) @end +(const byte) CH = (byte) ' +'pm (const byte*) MESSAGE[] = (byte*) "hello world"pm +(const byte*) SCREEN = (byte*) 1024 (void()) main() (byte~) main::$0 reg byte a 202.0 (label) main::@1 @@ -10,6 +13,7 @@ world"pm (label) main::@3 (label) main::@4 (label) main::@5 +(label) main::@6 (label) main::@return (byte*) main::cursor (byte*) main::cursor#1 cursor zp[2]:6 151.5 diff --git a/src/test/ref/string-escapes-5.asm b/src/test/ref/string-escapes-5.asm new file mode 100644 index 000000000..da3fd4577 --- /dev/null +++ b/src/test/ref/string-escapes-5.asm @@ -0,0 +1,30 @@ +// Test using some simple supported string escape +// Uses \xnn to add chars by hex-code that do not exist with the encoding. +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +.encoding "petscii_mixed" + .const CH = -1 +main: { + ldx #0 + __b1: + // while(MESSAGE[i]) + lda MESSAGE,x + cmp #0 + bne __b2 + // SCREEN[0x28] = CH + lda #CH + sta SCREEN+$28 + // } + rts + __b2: + // SCREEN[i] = MESSAGE[i++] + lda MESSAGE,x + sta SCREEN,x + // SCREEN[i] = MESSAGE[i++]; + inx + jmp __b1 +} + MESSAGE: .text @"qwe\$ffasd\$ferty" + .byte 0 diff --git a/src/test/ref/string-escapes-5.cfg b/src/test/ref/string-escapes-5.cfg new file mode 100644 index 000000000..0a682a36e --- /dev/null +++ b/src/test/ref/string-escapes-5.cfg @@ -0,0 +1,28 @@ +@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] phi() + 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) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH + to:main::@return +main::@return: scope:[main] from main::@3 + [8] return + to:@return +main::@2: scope:[main] from main::@1 + [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/string-escapes-5.log b/src/test/ref/string-escapes-5.log new file mode 100644 index 000000000..68c5b7a87 --- /dev/null +++ b/src/test/ref/string-escapes-5.log @@ -0,0 +1,398 @@ +Warning! Adding boolean cast to non-boolean condition *((const byte*) MESSAGE + (byte) main::i) +Identified constant variable (byte) CH +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + (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 ← (number) 0 != *((const byte*) MESSAGE + (byte) main::i#2) + if((bool~) main::$0) goto main::@2 + to:main::@3 +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + *((const nomodify byte*) SCREEN + (byte) main::i#3) ← *((const byte*) MESSAGE + (byte) main::i#3) + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@3: scope:[main] from main::@1 + *((const nomodify byte*) SCREEN + (number) $28) ← (const byte) CH + to:main::@return +main::@return: scope:[main] from main::@3 + 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 byte) CH = (byte) '刺'pm +(const byte*) MESSAGE[] = (byte*) "qwe刺asd茶rty"pm +(const nomodify byte*) SCREEN = (byte*)(number) $400 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(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 (bool~) main::$0 ← (number) 0 != *((const byte*) MESSAGE + (byte) main::i#2) +Adding number conversion cast (unumber) $28 in *((const nomodify byte*) SCREEN + (number) $28) ← (const byte) CH +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast $28 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $28 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [3] if((byte) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) 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 +Consolidated array index constant in *(SCREEN+$28) +Successful SSA optimization Pass2ConstantAdditionElimination +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 +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 +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() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + 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) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH + to:main::@return +main::@return: scope:[main] from main::@3 + [8] return + to:@return +main::@2: scope:[main] from main::@1 + [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) + [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 168.33333333333331 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test using some simple supported string escape +// Uses \xnn to add chars by hex-code that do not exist with the encoding. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 +.encoding "petscii_mixed" + .const CH = -1 + // @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::@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) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MESSAGE,y + bne __b2 + jmp __b3 + // main::@3 + __b3: + // [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$28 + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + // main::@2 + __b2: + // [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy.z i + lda MESSAGE,y + sta SCREEN,y + // [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 + MESSAGE: .text @"qwe\$ffasd\$ferty" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if((byte) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@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[1]:2 [ main::i#2 main::i#1 ] +Statement [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH [ ] ( main:2 [ ] { } ) always clobbers reg byte a +Statement [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [6] if((byte) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] { } ) always clobbers reg byte a +Statement [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH [ ] ( main:2 [ ] { } ) always clobbers reg byte a +Statement [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) [ main::i#2 ] ( main: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 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 370.33: zp[1]:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 342 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 342 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test using some simple supported string escape +// Uses \xnn to add chars by hex-code that do not exist with the encoding. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 +.encoding "petscii_mixed" + .const CH = -1 + // @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::@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) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuxx_then_la1 + lda MESSAGE,x + cmp #0 + bne __b2 + jmp __b3 + // main::@3 + __b3: + // [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$28 + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + // main::@2 + __b2: + // [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda MESSAGE,x + sta SCREEN,x + // [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 + MESSAGE: .text @"qwe\$ffasd\$ferty" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __b3 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction main_from___b1: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bend: +Removing instruction __b1_from_main: +Removing instruction __b3: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction __bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) CH = (byte) '刺'pm +(const byte*) MESSAGE[] = (byte*) "qwe刺asd茶rty"pm +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 202.0 +(byte) main::i#2 reg byte x 168.33333333333331 + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 267 + + // File Comments +// Test using some simple supported string escape +// Uses \xnn to add chars by hex-code that do not exist with the encoding. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 +.encoding "petscii_mixed" + .const CH = -1 + // @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::@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: + // while(MESSAGE[i]) + // [6] if((byte) 0!=*((const byte*) MESSAGE + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuxx_then_la1 + lda MESSAGE,x + cmp #0 + bne __b2 + // main::@3 + // SCREEN[0x28] = CH + // [7] *((const nomodify byte*) SCREEN+(byte) $28) ← (const byte) CH -- _deref_pbuc1=vbuc2 + lda #CH + sta SCREEN+$28 + // main::@return + // } + // [8] return + rts + // main::@2 + __b2: + // SCREEN[i] = MESSAGE[i++] + // [9] *((const nomodify byte*) SCREEN + (byte) main::i#2) ← *((const byte*) MESSAGE + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda MESSAGE,x + sta SCREEN,x + // SCREEN[i] = MESSAGE[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 + MESSAGE: .text @"qwe\$ffasd\$ferty" + .byte 0 + diff --git a/src/test/ref/string-escapes-5.sym b/src/test/ref/string-escapes-5.sym new file mode 100644 index 000000000..5948cd68e --- /dev/null +++ b/src/test/ref/string-escapes-5.sym @@ -0,0 +1,16 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) CH = (byte) '刺'pm +(const byte*) MESSAGE[] = (byte*) "qwe刺asd茶rty"pm +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 202.0 +(byte) main::i#2 reg byte x 168.33333333333331 + +reg byte x [ main::i#2 main::i#1 ]