mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-04-05 07:40:39 +00:00
Added support for octal character encoding in strings and literal chars. Closes #642
This commit is contained in:
parent
ef9e8761ed
commit
e328180d88
@ -3,8 +3,9 @@ package dk.camelot64.kickc.model.values;
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import kickass.nonasm.c64.CharToPetsciiConverter;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.PrimitiveIterator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** String encoding. */
|
||||
public enum StringEncoding {
|
||||
@ -15,8 +16,7 @@ public enum StringEncoding {
|
||||
SCREENCODE_UPPER("screencode_upper", "screencode_upper", "su", CharToPetsciiConverter.charToScreenCode_upper),
|
||||
ASCII("ascii", "ascii", "as", CharToPetsciiConverter.charToAscii),
|
||||
ATASCII("atascii", null, "at", CharToAtasciiConverter.charToAtascii),
|
||||
SCREENCODE_ATARI("screencode_atari", null, "sa", CharToAtasciiConverter.charToScreenCodeAtari)
|
||||
;
|
||||
SCREENCODE_ATARI("screencode_atari", null, "sa", CharToAtasciiConverter.charToScreenCodeAtari);
|
||||
|
||||
/** 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;
|
||||
@ -136,9 +136,9 @@ public enum StringEncoding {
|
||||
*/
|
||||
public String escapeToAscii(String stringValue) {
|
||||
StringBuilder stringResult = new StringBuilder();
|
||||
final PrimitiveIterator.OfInt escapedIterator = stringValue.chars().iterator();
|
||||
while(escapedIterator.hasNext()) {
|
||||
stringResult.append(escapeToAsciiFirst(escapedIterator));
|
||||
final LinkedList<Integer> escapedChars = new LinkedList<>(stringValue.chars().boxed().collect(Collectors.toList()));
|
||||
while(!escapedChars.isEmpty()) {
|
||||
stringResult.append(escapeToAsciiFirst(escapedChars));
|
||||
}
|
||||
return stringResult.toString();
|
||||
}
|
||||
@ -151,13 +151,13 @@ public enum StringEncoding {
|
||||
* @param escapedCharsIterator The characters of the string to parse one char from. The iterator is moved beyond any handled chars.
|
||||
* @return The first ASCII character of the list.
|
||||
*/
|
||||
public char escapeToAsciiFirst(PrimitiveIterator.OfInt escapedCharsIterator) {
|
||||
char stringChar = (char) escapedCharsIterator.nextInt();
|
||||
public char escapeToAsciiFirst(LinkedList<Integer> escapedCharsIterator) {
|
||||
char stringChar = (char) escapedCharsIterator.pop().intValue();
|
||||
if(stringChar != '\\')
|
||||
return stringChar;
|
||||
// Escape started - handle it!
|
||||
if(!escapedCharsIterator.hasNext()) throw new CompileError("Unfinished string escape sequence at end of string");
|
||||
char escapeChar = (char) escapedCharsIterator.nextInt();
|
||||
if(escapedCharsIterator.isEmpty()) throw new CompileError("Unfinished string escape sequence at end of string");
|
||||
char escapeChar = (char) escapedCharsIterator.pop().intValue();
|
||||
switch(escapeChar) {
|
||||
case 'n':
|
||||
return '\n';
|
||||
@ -165,8 +165,6 @@ public enum StringEncoding {
|
||||
return '\r';
|
||||
case 'f':
|
||||
return '\f';
|
||||
case '0':
|
||||
return '\0';
|
||||
case '"':
|
||||
return '"';
|
||||
case '\'':
|
||||
@ -175,10 +173,28 @@ public enum StringEncoding {
|
||||
return '\\';
|
||||
case 'x':
|
||||
String hexNum = "";
|
||||
hexNum += (char) escapedCharsIterator.nextInt();
|
||||
hexNum += (char) escapedCharsIterator.nextInt();
|
||||
hexNum += (char) escapedCharsIterator.pop().intValue();
|
||||
hexNum += (char) escapedCharsIterator.pop().intValue();
|
||||
final byte hexEncoding = (byte) Integer.parseInt(hexNum, 16);
|
||||
return charFromEncoded(hexEncoding);
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
String octalNum = "" + escapeChar;
|
||||
while(octalNum.length() < 3) {
|
||||
final Integer peek = escapedCharsIterator.peek();
|
||||
if(peek != null && peek >= '0' && peek <= '7') {
|
||||
octalNum += (char) escapedCharsIterator.pop().intValue();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
final byte octalEncoding = (byte) Integer.parseInt(octalNum, 8);
|
||||
return charFromEncoded(octalEncoding);
|
||||
default:
|
||||
throw new CompileError("Illegal string escape sequence \\" + escapeChar);
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ fragment NAME_CHAR : [a-zA-Z0-9_];
|
||||
|
||||
// Strings and chars
|
||||
STRING : '"' ('\\"' | ~'"')* '"' [z]?([aps][tsmua]?)?[z]? ;
|
||||
CHAR : '\'' ('\\'(['"rfn\\]|'x'[0-9a-f][0-9a-f]) | ~'\'' ) '\'';
|
||||
CHAR : '\'' ('\\'(['"rfn\\0-9][0-9]?[0-9]?|'x'[0-9a-f][0-9a-f]) | ~'\'' ) '\'';
|
||||
|
||||
// White space on hidden channel 1
|
||||
WS : [ \t\r\n\u00a0]+ -> channel(1);
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ import java.io.IOException;
|
||||
*/
|
||||
public class TestProgramsFast extends TestPrograms {
|
||||
|
||||
|
||||
@Test
|
||||
public void testTmpZpProblem() throws IOException {
|
||||
compileAndCompare("tmp-zp-problem.c");
|
||||
@ -1676,6 +1677,21 @@ public class TestProgramsFast extends TestPrograms {
|
||||
assertError("string-escapes-err-0.c", "Unfinished string escape sequence at end of string");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringEscapes10() throws IOException {
|
||||
compileAndCompare("string-escapes-10.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringEscapes9() throws IOException {
|
||||
compileAndCompare("string-escapes-9.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringEscapes8() throws IOException {
|
||||
compileAndCompare("string-escapes-8.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringEscapes7() throws IOException {
|
||||
compileAndCompare("string-escapes-7.c");
|
||||
|
14
src/test/kc/string-escapes-10.c
Normal file
14
src/test/kc/string-escapes-10.c
Normal file
@ -0,0 +1,14 @@
|
||||
// Test octal escapes in chars
|
||||
|
||||
char MSG1 = '\1';
|
||||
char MSG2 = '\02';
|
||||
char MSG3 = '\003';
|
||||
char MSG4 = '\377';
|
||||
char* SCREEN = (char*)0x0400;
|
||||
|
||||
void main() {
|
||||
SCREEN[0] = MSG1;
|
||||
SCREEN[1] = MSG2;
|
||||
SCREEN[2] = MSG3;
|
||||
SCREEN[3] = MSG4;
|
||||
}
|
5
src/test/kc/string-escapes-8.c
Normal file
5
src/test/kc/string-escapes-8.c
Normal file
@ -0,0 +1,5 @@
|
||||
// Support for '\0' literal character
|
||||
char * const SCREEN = (char*)0x0400;
|
||||
void main(void) {
|
||||
SCREEN[0] = '\0';
|
||||
}
|
10
src/test/kc/string-escapes-9.c
Normal file
10
src/test/kc/string-escapes-9.c
Normal file
@ -0,0 +1,10 @@
|
||||
// Test octal escapes in strings
|
||||
|
||||
char MESSAGE[] = "\1\01\001\377";
|
||||
char* SCREEN = (char*)0x0400;
|
||||
|
||||
void main() {
|
||||
byte i=0;
|
||||
while(MESSAGE[i])
|
||||
SCREEN[i] = MESSAGE[i++];
|
||||
}
|
31
src/test/ref/string-escapes-10.asm
Normal file
31
src/test/ref/string-escapes-10.asm
Normal file
@ -0,0 +1,31 @@
|
||||
// Test octal escapes in chars
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-10.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)
|
||||
.const MSG1 = 'a'
|
||||
.const MSG2 = 'b'
|
||||
.const MSG3 = 'c'
|
||||
.const MSG4 = '\$ff'
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
main: {
|
||||
// SCREEN[0] = MSG1
|
||||
lda #MSG1
|
||||
sta SCREEN
|
||||
// SCREEN[1] = MSG2
|
||||
lda #MSG2
|
||||
sta SCREEN+1
|
||||
// SCREEN[2] = MSG3
|
||||
lda #MSG3
|
||||
sta SCREEN+2
|
||||
// SCREEN[3] = MSG4
|
||||
lda #MSG4
|
||||
sta SCREEN+3
|
||||
// }
|
||||
rts
|
||||
}
|
11
src/test/ref/string-escapes-10.cfg
Normal file
11
src/test/ref/string-escapes-10.cfg
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] *SCREEN = MSG1
|
||||
[1] *(SCREEN+1) = MSG2
|
||||
[2] *(SCREEN+2) = MSG3
|
||||
[3] *(SCREEN+3) = MSG4
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[4] return
|
||||
to:@return
|
207
src/test/ref/string-escapes-10.log
Normal file
207
src/test/ref/string-escapes-10.log
Normal file
@ -0,0 +1,207 @@
|
||||
Inlined call call __init
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
|
||||
void main()
|
||||
main: scope:[main] from __start::@1
|
||||
SCREEN[0] = MSG1
|
||||
SCREEN[1] = MSG2
|
||||
SCREEN[2] = MSG3
|
||||
SCREEN[3] = MSG4
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
return
|
||||
to:@return
|
||||
|
||||
void __start()
|
||||
__start: scope:[__start] from
|
||||
to:__start::__init1
|
||||
__start::__init1: scope:[__start] from __start
|
||||
to:__start::@1
|
||||
__start::@1: scope:[__start] from __start::__init1
|
||||
call main
|
||||
to:__start::@2
|
||||
__start::@2: scope:[__start] from __start::@1
|
||||
to:__start::@return
|
||||
__start::@return: scope:[__start] from __start::@2
|
||||
return
|
||||
to:@return
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
__constant char MSG1 = 'a'
|
||||
__constant char MSG2 = 'b'
|
||||
__constant char MSG3 = 'c'
|
||||
__constant char MSG4 = '刺'
|
||||
__constant char *SCREEN = (char *)$400
|
||||
void __start()
|
||||
void main()
|
||||
|
||||
Adding number conversion cast (unumber) 0 in SCREEN[0] = MSG1
|
||||
Adding number conversion cast (unumber) 1 in SCREEN[1] = MSG2
|
||||
Adding number conversion cast (unumber) 2 in SCREEN[2] = MSG3
|
||||
Adding number conversion cast (unumber) 3 in SCREEN[3] = MSG4
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Simplifying constant pointer cast (char *) 1024
|
||||
Simplifying constant integer cast 0
|
||||
Simplifying constant integer cast 1
|
||||
Simplifying constant integer cast 2
|
||||
Simplifying constant integer cast 3
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (char) 0
|
||||
Finalized unsigned number type (char) 1
|
||||
Finalized unsigned number type (char) 2
|
||||
Finalized unsigned number type (char) 3
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Simplifying expression containing zero SCREEN in [0] SCREEN[0] = MSG1
|
||||
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||
Removing unused procedure __start
|
||||
Removing unused procedure block __start
|
||||
Removing unused procedure block __start::__init1
|
||||
Removing unused procedure block __start::@1
|
||||
Removing unused procedure block __start::@2
|
||||
Removing unused procedure block __start::@return
|
||||
Successful SSA optimization PassNEliminateEmptyStart
|
||||
Consolidated array index constant in *(SCREEN+1)
|
||||
Consolidated array index constant in *(SCREEN+2)
|
||||
Consolidated array index constant in *(SCREEN+3)
|
||||
Successful SSA optimization Pass2ConstantAdditionElimination
|
||||
CALL GRAPH
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] *SCREEN = MSG1
|
||||
[1] *(SCREEN+1) = MSG2
|
||||
[2] *(SCREEN+2) = MSG3
|
||||
[3] *(SCREEN+3) = MSG4
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[4] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
void main()
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [0] *SCREEN = MSG1 [ ] ( [ ] { } ) always clobbers reg byte a
|
||||
Statement [1] *(SCREEN+1) = MSG2 [ ] ( [ ] { } ) always clobbers reg byte a
|
||||
Statement [2] *(SCREEN+2) = MSG3 [ ] ( [ ] { } ) always clobbers reg byte a
|
||||
Statement [3] *(SCREEN+3) = MSG4 [ ] ( [ ] { } ) always clobbers reg byte a
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 33 combination
|
||||
Uplifting [] best 33 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Test octal escapes in chars
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-10.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)
|
||||
// Global Constants & labels
|
||||
.const MSG1 = 'a'
|
||||
.const MSG2 = 'b'
|
||||
.const MSG3 = 'c'
|
||||
.const MSG4 = '\$ff'
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// [0] *SCREEN = MSG1 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG1
|
||||
sta SCREEN
|
||||
// [1] *(SCREEN+1) = MSG2 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG2
|
||||
sta SCREEN+1
|
||||
// [2] *(SCREEN+2) = MSG3 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG3
|
||||
sta SCREEN+2
|
||||
// [3] *(SCREEN+3) = MSG4 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG4
|
||||
sta SCREEN+3
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [4] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
__constant char MSG1 = 'a'
|
||||
__constant char MSG2 = 'b'
|
||||
__constant char MSG3 = 'c'
|
||||
__constant char MSG4 = '刺'
|
||||
__constant char *SCREEN = (char *) 1024
|
||||
void main()
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 30
|
||||
|
||||
// File Comments
|
||||
// Test octal escapes in chars
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-10.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)
|
||||
// Global Constants & labels
|
||||
.const MSG1 = 'a'
|
||||
.const MSG2 = 'b'
|
||||
.const MSG3 = 'c'
|
||||
.const MSG4 = '\$ff'
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// SCREEN[0] = MSG1
|
||||
// [0] *SCREEN = MSG1 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG1
|
||||
sta SCREEN
|
||||
// SCREEN[1] = MSG2
|
||||
// [1] *(SCREEN+1) = MSG2 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG2
|
||||
sta SCREEN+1
|
||||
// SCREEN[2] = MSG3
|
||||
// [2] *(SCREEN+2) = MSG3 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG3
|
||||
sta SCREEN+2
|
||||
// SCREEN[3] = MSG4
|
||||
// [3] *(SCREEN+3) = MSG4 -- _deref_pbuc1=vbuc2
|
||||
lda #MSG4
|
||||
sta SCREEN+3
|
||||
// main::@return
|
||||
// }
|
||||
// [4] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
7
src/test/ref/string-escapes-10.sym
Normal file
7
src/test/ref/string-escapes-10.sym
Normal file
@ -0,0 +1,7 @@
|
||||
__constant char MSG1 = 'a'
|
||||
__constant char MSG2 = 'b'
|
||||
__constant char MSG3 = 'c'
|
||||
__constant char MSG4 = '刺'
|
||||
__constant char *SCREEN = (char *) 1024
|
||||
void main()
|
||||
|
@ -39,5 +39,5 @@ main: {
|
||||
jmp __b2
|
||||
}
|
||||
.segment Data
|
||||
MESSAGE: .text @"a\$00bb\$00ccc\$00"
|
||||
MESSAGE: .text "a@bb@ccc@"
|
||||
.byte 0
|
||||
|
Binary file not shown.
Binary file not shown.
18
src/test/ref/string-escapes-8.asm
Normal file
18
src/test/ref/string-escapes-8.asm
Normal file
@ -0,0 +1,18 @@
|
||||
// Support for '\0' literal character
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-8.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)
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
main: {
|
||||
// SCREEN[0] = '\0'
|
||||
lda #'@'
|
||||
sta SCREEN
|
||||
// }
|
||||
rts
|
||||
}
|
8
src/test/ref/string-escapes-8.cfg
Normal file
8
src/test/ref/string-escapes-8.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] *SCREEN = '@'
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[1] return
|
||||
to:@return
|
141
src/test/ref/string-escapes-8.log
Normal file
141
src/test/ref/string-escapes-8.log
Normal file
@ -0,0 +1,141 @@
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
|
||||
void main()
|
||||
main: scope:[main] from __start
|
||||
SCREEN[0] = '@'
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
return
|
||||
to:@return
|
||||
|
||||
void __start()
|
||||
__start: scope:[__start] from
|
||||
call main
|
||||
to:__start::@1
|
||||
__start::@1: scope:[__start] from __start
|
||||
to:__start::@return
|
||||
__start::@return: scope:[__start] from __start::@1
|
||||
return
|
||||
to:@return
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
__constant char * const SCREEN = (char *)$400
|
||||
void __start()
|
||||
void main()
|
||||
|
||||
Adding number conversion cast (unumber) 0 in SCREEN[0] = '@'
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Simplifying constant pointer cast (char *) 1024
|
||||
Simplifying constant integer cast 0
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (char) 0
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Simplifying expression containing zero SCREEN in [0] SCREEN[0] = '@'
|
||||
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||
Removing unused procedure __start
|
||||
Removing unused procedure block __start
|
||||
Removing unused procedure block __start::@1
|
||||
Removing unused procedure block __start::@return
|
||||
Successful SSA optimization PassNEliminateEmptyStart
|
||||
CALL GRAPH
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] *SCREEN = '@'
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[1] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
void main()
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [0] *SCREEN = '@' [ ] ( [ ] { } ) always clobbers reg byte a
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 15 combination
|
||||
Uplifting [] best 15 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Support for '\0' literal character
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-8.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)
|
||||
// Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// [0] *SCREEN = '@' -- _deref_pbuc1=vbuc2
|
||||
lda #'@'
|
||||
sta SCREEN
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [1] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
__constant char * const SCREEN = (char *) 1024
|
||||
void main()
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 12
|
||||
|
||||
// File Comments
|
||||
// Support for '\0' literal character
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-8.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)
|
||||
// Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// SCREEN[0] = '\0'
|
||||
// [0] *SCREEN = '@' -- _deref_pbuc1=vbuc2
|
||||
lda #'@'
|
||||
sta SCREEN
|
||||
// main::@return
|
||||
// }
|
||||
// [1] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
3
src/test/ref/string-escapes-8.sym
Normal file
3
src/test/ref/string-escapes-8.sym
Normal file
@ -0,0 +1,3 @@
|
||||
__constant char * const SCREEN = (char *) 1024
|
||||
void main()
|
||||
|
31
src/test/ref/string-escapes-9.asm
Normal file
31
src/test/ref/string-escapes-9.asm
Normal file
@ -0,0 +1,31 @@
|
||||
// Test octal escapes in strings
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-9.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)
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
main: {
|
||||
ldx #0
|
||||
__b1:
|
||||
// while(MESSAGE[i])
|
||||
lda MESSAGE,x
|
||||
cmp #0
|
||||
bne __b2
|
||||
// }
|
||||
rts
|
||||
__b2:
|
||||
// SCREEN[i] = MESSAGE[i++]
|
||||
lda MESSAGE,x
|
||||
sta SCREEN,x
|
||||
// SCREEN[i] = MESSAGE[i++];
|
||||
inx
|
||||
jmp __b1
|
||||
}
|
||||
.segment Data
|
||||
MESSAGE: .text @"aaa\$ff"
|
||||
.byte 0
|
16
src/test/ref/string-escapes-9.cfg
Normal file
16
src/test/ref/string-escapes-9.cfg
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
[1] main::i#2 = phi( main/0, main::@2/main::i#1 )
|
||||
[2] if(0!=MESSAGE[main::i#2]) goto main::@2
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@1
|
||||
[3] return
|
||||
to:@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
[4] SCREEN[main::i#2] = MESSAGE[main::i#2]
|
||||
[5] main::i#1 = ++ main::i#2
|
||||
to:main::@1
|
245
src/test/ref/string-escapes-9.log
Normal file
245
src/test/ref/string-escapes-9.log
Normal file
@ -0,0 +1,245 @@
|
||||
Inlined call call __init
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
|
||||
void main()
|
||||
main: scope:[main] from __start::@1
|
||||
main::i#0 = 0
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
main::i#2 = phi( main/main::i#0, main::@2/main::i#1 )
|
||||
main::$0 = 0 != MESSAGE[main::i#2]
|
||||
if(main::$0) goto main::@2
|
||||
to:main::@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
main::i#3 = phi( main::@1/main::i#2 )
|
||||
SCREEN[main::i#3] = MESSAGE[main::i#3]
|
||||
main::i#1 = ++ main::i#3
|
||||
to:main::@1
|
||||
main::@return: scope:[main] from main::@1
|
||||
return
|
||||
to:@return
|
||||
|
||||
void __start()
|
||||
__start: scope:[__start] from
|
||||
to:__start::__init1
|
||||
__start::__init1: scope:[__start] from __start
|
||||
to:__start::@1
|
||||
__start::@1: scope:[__start] from __start::__init1
|
||||
call main
|
||||
to:__start::@2
|
||||
__start::@2: scope:[__start] from __start::@1
|
||||
to:__start::@return
|
||||
__start::@return: scope:[__start] from __start::@2
|
||||
return
|
||||
to:@return
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
__constant char MESSAGE[] = "aaa刺"
|
||||
__constant char *SCREEN = (char *)$400
|
||||
void __start()
|
||||
void main()
|
||||
bool main::$0
|
||||
char main::i
|
||||
char main::i#0
|
||||
char main::i#1
|
||||
char main::i#2
|
||||
char main::i#3
|
||||
|
||||
Adding number conversion cast (unumber) 0 in main::$0 = 0 != MESSAGE[main::i#2]
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Simplifying constant pointer cast (char *) 1024
|
||||
Simplifying constant integer cast 0
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (char) 0
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Alias main::i#2 = main::i#3
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Simple Condition main::$0 [3] if(0!=MESSAGE[main::i#2]) goto main::@2
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant main::i#0 = 0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Removing unused procedure __start
|
||||
Removing unused procedure block __start
|
||||
Removing unused procedure block __start::__init1
|
||||
Removing unused procedure block __start::@1
|
||||
Removing unused procedure block __start::@2
|
||||
Removing unused procedure block __start::@return
|
||||
Successful SSA optimization PassNEliminateEmptyStart
|
||||
Inlining constant with var siblings main::i#0
|
||||
Constant inlined main::i#0 = 0
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Adding NOP phi() at start of main
|
||||
CALL GRAPH
|
||||
|
||||
Created 1 initial phi equivalence classes
|
||||
Coalesced [6] main::i#4 = main::i#1
|
||||
Coalesced down to 1 phi equivalence classes
|
||||
Adding NOP phi() at start of main
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
|
||||
void main()
|
||||
main: scope:[main] from
|
||||
[0] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
[1] main::i#2 = phi( main/0, main::@2/main::i#1 )
|
||||
[2] if(0!=MESSAGE[main::i#2]) goto main::@2
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@1
|
||||
[3] return
|
||||
to:@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
[4] SCREEN[main::i#2] = MESSAGE[main::i#2]
|
||||
[5] main::i#1 = ++ main::i#2
|
||||
to:main::@1
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
void main()
|
||||
char main::i
|
||||
char main::i#1 // 22.0
|
||||
char main::i#2 // 18.333333333333332
|
||||
|
||||
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 ]
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [2] if(0!=MESSAGE[main::i#2]) goto main::@2 [ main::i#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 [4] SCREEN[main::i#2] = MESSAGE[main::i#2] [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
|
||||
Statement [2] if(0!=MESSAGE[main::i#2]) goto main::@2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
|
||||
Statement [4] SCREEN[main::i#2] = MESSAGE[main::i#2] [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
|
||||
Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y ,
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main] 40.33: zp[1]:2 [ main::i#2 main::i#1 ]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 321 combination reg byte x [ main::i#2 main::i#1 ]
|
||||
Uplifting [] best 321 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Test octal escapes in strings
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-9.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)
|
||||
// Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// [1] phi from main to main::@1 [phi:main->main::@1]
|
||||
__b1_from_main:
|
||||
// [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
jmp __b1
|
||||
// main::@1
|
||||
__b1:
|
||||
// [2] if(0!=MESSAGE[main::i#2]) goto main::@2 -- 0_neq_pbuc1_derefidx_vbuxx_then_la1
|
||||
lda MESSAGE,x
|
||||
cmp #0
|
||||
bne __b2
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [3] return
|
||||
rts
|
||||
// main::@2
|
||||
__b2:
|
||||
// [4] SCREEN[main::i#2] = MESSAGE[main::i#2] -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx
|
||||
lda MESSAGE,x
|
||||
sta SCREEN,x
|
||||
// [5] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
|
||||
__b1_from___b2:
|
||||
// [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy
|
||||
jmp __b1
|
||||
}
|
||||
// File Data
|
||||
.segment Data
|
||||
MESSAGE: .text @"aaa\$ff"
|
||||
.byte 0
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __b1_from_main:
|
||||
Removing instruction __breturn:
|
||||
Removing instruction __b1_from___b2:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
__constant char MESSAGE[] = "aaa刺"
|
||||
__constant char *SCREEN = (char *) 1024
|
||||
void main()
|
||||
char main::i
|
||||
char main::i#1 // reg byte x 22.0
|
||||
char main::i#2 // reg byte x 18.333333333333332
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 261
|
||||
|
||||
// File Comments
|
||||
// Test octal escapes in strings
|
||||
// Upstart
|
||||
// Commodore 64 PRG executable file
|
||||
.file [name="string-escapes-9.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)
|
||||
// Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
.segment Code
|
||||
// main
|
||||
main: {
|
||||
// [1] phi from main to main::@1 [phi:main->main::@1]
|
||||
// [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
// main::@1
|
||||
__b1:
|
||||
// while(MESSAGE[i])
|
||||
// [2] if(0!=MESSAGE[main::i#2]) goto main::@2 -- 0_neq_pbuc1_derefidx_vbuxx_then_la1
|
||||
lda MESSAGE,x
|
||||
cmp #0
|
||||
bne __b2
|
||||
// main::@return
|
||||
// }
|
||||
// [3] return
|
||||
rts
|
||||
// main::@2
|
||||
__b2:
|
||||
// SCREEN[i] = MESSAGE[i++]
|
||||
// [4] SCREEN[main::i#2] = MESSAGE[main::i#2] -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx
|
||||
lda MESSAGE,x
|
||||
sta SCREEN,x
|
||||
// SCREEN[i] = MESSAGE[i++];
|
||||
// [5] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
|
||||
// [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy
|
||||
jmp __b1
|
||||
}
|
||||
// File Data
|
||||
.segment Data
|
||||
MESSAGE: .text @"aaa\$ff"
|
||||
.byte 0
|
||||
|
8
src/test/ref/string-escapes-9.sym
Normal file
8
src/test/ref/string-escapes-9.sym
Normal file
@ -0,0 +1,8 @@
|
||||
__constant char MESSAGE[] = "aaa刺"
|
||||
__constant char *SCREEN = (char *) 1024
|
||||
void main()
|
||||
char main::i
|
||||
char main::i#1 // reg byte x 22.0
|
||||
char main::i#2 // reg byte x 18.333333333333332
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
Loading…
x
Reference in New Issue
Block a user