diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastPtr.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastPtr.java index 3a0e19a4a..e2e295acc 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastPtr.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastPtr.java @@ -1,12 +1,14 @@ package dk.camelot64.kickc.model.operators; -import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ConstantNotLiteral; +import dk.camelot64.kickc.model.InternalError; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypePointer; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; import dk.camelot64.kickc.model.values.ConstantPointer; +import dk.camelot64.kickc.model.values.ConstantString; /** Unary Cast to a pointer ( type* ) */ public class OperatorCastPtr extends OperatorCast { @@ -24,8 +26,10 @@ public class OperatorCastPtr extends OperatorCast { return new ConstantPointer(((ConstantInteger) value).getInteger(), pointerType.getElementType()); } else if(value instanceof ConstantPointer) { return new ConstantPointer(((ConstantPointer) value).getLocation(), pointerType.getElementType()); + } else if(value instanceof ConstantString){ + throw new ConstantNotLiteral("Constant string not literal"); } - throw new CompileError("Calculation not implemented " + getOperator() + " " + value); + throw new InternalError("Calculation not implemented " + getOperator() + " " + value); } @Override diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java index 1ee34c0fe..e874378a0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java @@ -58,6 +58,16 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization { getLog().append("Adding pointer type conversion cast to void pointer (" + leftType + ") " + binary.getRight().toString() + " in " + currentStmt.toString(getProgram(), false)); binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getScope()); modified.set(true); + } else if((leftType instanceof SymbolTypePointer) && SymbolType.STRING.equals(rightType) && SymbolType.VOID.equals(((SymbolTypePointer) leftType).getElementType())) { + if(pass2 || getLog().isVerbosePass1CreateSsa()) + getLog().append("Adding void pointer type conversion cast (" + leftType + ") " + binary.getRight().toString() + " in " + currentStmt.toString(getProgram(), false)); + binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getScope()); + modified.set(true); + } else if(leftType.equals(SymbolType.STRING) && rightType instanceof SymbolTypePointer && SymbolType.VOID.equals(((SymbolTypePointer) rightType).getElementType())) { + if(pass2 || getLog().isVerbosePass1CreateSsa()) + getLog().append("Adding pointer type conversion cast to void pointer (" + leftType + ") " + binary.getRight().toString() + " in " + currentStmt.toString(getProgram(), false)); + binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getScope()); + modified.set(true); } else if(SymbolType.WORD.equals(leftType) && isLiteralWordCandidate(right)) { // Detect word literal constructor SymbolType conversionType = SymbolType.WORD; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 49ff98a76..ac4535c10 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -377,13 +377,10 @@ public class TestPrograms { compileAndCompare("font-hex-show"); } - // TODO: Fix string not converted to void* properly https://gitlab.com/camelot/kickc/issues/281 - /* @Test public void testMemcpy1() throws IOException, URISyntaxException { - compileAndCompare("memcpy-1", log()); + compileAndCompare("memcpy-1"); } - */ @Test public void testMemcpy0() throws IOException, URISyntaxException { diff --git a/src/test/kc/memcpy-1.kc b/src/test/kc/memcpy-1.kc index 89d3754e1..b9e8a0475 100644 --- a/src/test/kc/memcpy-1.kc +++ b/src/test/kc/memcpy-1.kc @@ -18,7 +18,6 @@ void main() { *sc2++ = *reigns++; } - // Not working memcpy(SCREEN+10, CAMELOT, 7); memcpy(SCREEN+50, "rules", 5); diff --git a/src/test/ref/memcpy-1.asm b/src/test/ref/memcpy-1.asm new file mode 100644 index 000000000..f49894205 --- /dev/null +++ b/src/test/ref/memcpy-1.asm @@ -0,0 +1,131 @@ +// Test memcpy on strings ( +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + .label sc = 6 + .label camelot = 4 + .label sc2 = 2 + .label reigns = 8 + ldx #0 + lda #SCREEN + sta.z sc+1 + lda #CAMELOT + sta.z camelot+1 + b1: + ldy #0 + lda (camelot),y + sta (sc),y + inc.z sc + bne !+ + inc.z sc+1 + !: + inc.z camelot + bne !+ + inc.z camelot+1 + !: + inx + cpx #7 + bne b1 + ldx #0 + lda #SCREEN+$28 + sta.z sc2+1 + lda #reigns_0 + sta.z reigns+1 + b2: + ldy #0 + lda (reigns),y + sta (sc2),y + inc.z sc2 + bne !+ + inc.z sc2+1 + !: + inc.z reigns + bne !+ + inc.z reigns+1 + !: + inx + cpx #6 + bne b2 + lda #<7 + sta.z memcpy.num + lda #>7 + sta.z memcpy.num+1 + lda #SCREEN+$a + sta.z memcpy.destination+1 + lda #CAMELOT + sta.z memcpy.source+1 + jsr memcpy + lda #<5 + sta.z memcpy.num + lda #>5 + sta.z memcpy.num+1 + lda #SCREEN+$32 + sta.z memcpy.destination+1 + lda #<_8 + sta.z memcpy.source + lda #>_8 + sta.z memcpy.source+1 + jsr memcpy + rts + _8: .text "rules" + .byte 0 + reigns_0: .text "reigns" + .byte 0 +} +// Copy block of memory (forwards) +// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. +// memcpy(void* zeropage(6) destination, void* zeropage(4) source, word zeropage(8) num) +memcpy: { + .label src_end = 8 + .label dst = 6 + .label src = 4 + .label source = 4 + .label destination = 6 + .label num = 8 + lda.z src_end + clc + adc.z source + sta.z src_end + lda.z src_end+1 + adc.z source+1 + sta.z src_end+1 + b1: + lda.z src+1 + cmp.z src_end+1 + bne b2 + lda.z src + cmp.z src_end + bne b2 + rts + b2: + ldy #0 + lda (src),y + sta (dst),y + inc.z dst + bne !+ + inc.z dst+1 + !: + inc.z src + bne !+ + inc.z src+1 + !: + jmp b1 +} + CAMELOT: .text "camelot" + .byte 0 diff --git a/src/test/ref/memcpy-1.cfg b/src/test/ref/memcpy-1.cfg new file mode 100644 index 000000000..3d07f7559 --- /dev/null +++ b/src/test/ref/memcpy-1.cfg @@ -0,0 +1,64 @@ +@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::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 ) + [5] (byte*) main::sc#2 ← phi( main/(const byte*) SCREEN#0 main::@1/(byte*) main::sc#1 ) + [5] (byte*) main::camelot#2 ← phi( main/(const byte[]) CAMELOT#0 main::@1/(byte*) main::camelot#1 ) + [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) + [7] (byte*) main::sc#1 ← ++ (byte*) main::sc#2 + [8] (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte) 7) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [11] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [11] (byte*) main::sc2#2 ← phi( main::@1/(const byte*) SCREEN#0+(byte) $28 main::@2/(byte*) main::sc2#1 ) + [11] (byte*) main::reigns#2 ← phi( main::@1/(const byte*) main::reigns#0 main::@2/(byte*) main::reigns#1 ) + [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) + [13] (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 + [14] (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 + [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [16] if((byte) main::i1#1!=(byte) 6) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [17] phi() + [18] call memcpy + to:main::@4 +main::@4: scope:[main] from main::@3 + [19] phi() + [20] call memcpy + to:main::@return +main::@return: scope:[main] from main::@4 + [21] return + to:@return +memcpy: scope:[memcpy] from main::@3 main::@4 + [22] (word) memcpy::num#2 ← phi( main::@3/(byte) 7 main::@4/(byte) 5 ) + [22] (void*) memcpy::destination#2 ← phi( main::@3/(void*)(const byte*) SCREEN#0+(byte) $a main::@4/(void*)(const byte*) SCREEN#0+(byte) $32 ) + [22] (void*) memcpy::source#2 ← phi( main::@3/(void*)(const byte[]) CAMELOT#0 main::@4/(void*)(const string) main::$8 ) + [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 + [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 + [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 + to:memcpy::@1 +memcpy::@1: scope:[memcpy] from memcpy memcpy::@2 + [26] (byte*) memcpy::dst#2 ← phi( memcpy/(byte*~) memcpy::dst#4 memcpy::@2/(byte*) memcpy::dst#1 ) + [26] (byte*) memcpy::src#2 ← phi( memcpy/(byte*~) memcpy::src#4 memcpy::@2/(byte*) memcpy::src#1 ) + [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 + to:memcpy::@return +memcpy::@return: scope:[memcpy] from memcpy::@1 + [28] return + to:@return +memcpy::@2: scope:[memcpy] from memcpy::@1 + [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) + [30] (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 + [31] (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#2 + to:memcpy::@1 diff --git a/src/test/ref/memcpy-1.log b/src/test/ref/memcpy-1.log new file mode 100644 index 000000000..da1a2b5f4 --- /dev/null +++ b/src/test/ref/memcpy-1.log @@ -0,0 +1,1394 @@ +Warning! Adding boolean cast to non-boolean condition *((byte*) strcpy::src) +Culled Empty Block (label) memcpy::@4 +Culled Empty Block (label) memcpy::@5 +Culled Empty Block (label) memcpy::@6 +Culled Empty Block (label) memcpy::@7 +Culled Empty Block (label) @1 +Culled Empty Block (label) @2 +Culled Empty Block (label) @3 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@4 +memcpy: scope:[memcpy] from main::@4 main::@5 + (word) memcpy::num#2 ← phi( main::@4/(word) memcpy::num#0 main::@5/(word) memcpy::num#1 ) + (void*) memcpy::destination#2 ← phi( main::@4/(void*) memcpy::destination#0 main::@5/(void*) memcpy::destination#1 ) + (void*) memcpy::source#2 ← phi( main::@4/(void*) memcpy::source#0 main::@5/(void*) memcpy::source#1 ) + (byte*) memcpy::src#0 ← ((byte*)) (void*) memcpy::source#2 + (byte*) memcpy::dst#0 ← ((byte*)) (void*) memcpy::destination#2 + (byte*~) memcpy::$0 ← ((byte*)) (void*) memcpy::source#2 + (byte*~) memcpy::$1 ← (byte*~) memcpy::$0 + (word) memcpy::num#2 + (byte*) memcpy::src_end#0 ← (byte*~) memcpy::$1 + to:memcpy::@1 +memcpy::@1: scope:[memcpy] from memcpy memcpy::@2 + (void*) memcpy::destination#4 ← phi( memcpy/(void*) memcpy::destination#2 memcpy::@2/(void*) memcpy::destination#5 ) + (byte*) memcpy::dst#3 ← phi( memcpy/(byte*) memcpy::dst#0 memcpy::@2/(byte*) memcpy::dst#1 ) + (byte*) memcpy::src_end#1 ← phi( memcpy/(byte*) memcpy::src_end#0 memcpy::@2/(byte*) memcpy::src_end#2 ) + (byte*) memcpy::src#2 ← phi( memcpy/(byte*) memcpy::src#0 memcpy::@2/(byte*) memcpy::src#1 ) + (bool~) memcpy::$2 ← (byte*) memcpy::src#2 != (byte*) memcpy::src_end#1 + if((bool~) memcpy::$2) goto memcpy::@2 + to:memcpy::@3 +memcpy::@2: scope:[memcpy] from memcpy::@1 + (void*) memcpy::destination#5 ← phi( memcpy::@1/(void*) memcpy::destination#4 ) + (byte*) memcpy::src_end#2 ← phi( memcpy::@1/(byte*) memcpy::src_end#1 ) + (byte*) memcpy::dst#2 ← phi( memcpy::@1/(byte*) memcpy::dst#3 ) + (byte*) memcpy::src#3 ← phi( memcpy::@1/(byte*) memcpy::src#2 ) + *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#3) + (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 + (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#3 + to:memcpy::@1 +memcpy::@3: scope:[memcpy] from memcpy::@1 + (void*) memcpy::destination#3 ← phi( memcpy::@1/(void*) memcpy::destination#4 ) + (void*) memcpy::return#0 ← (void*) memcpy::destination#3 + to:memcpy::@return +memcpy::@return: scope:[memcpy] from memcpy::@3 + (void*) memcpy::return#4 ← phi( memcpy::@3/(void*) memcpy::return#0 ) + (void*) memcpy::return#1 ← (void*) memcpy::return#4 + return + to:@return +@4: scope:[] from @begin + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + (byte[]) CAMELOT#0 ← (const string) $0 + to:@5 +main: scope:[main] from @5 + (byte*) main::sc#0 ← (byte*) SCREEN#0 + (byte*) main::camelot#0 ← (byte[]) CAMELOT#0 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@1/(byte) main::i#1 ) + (byte*) main::sc#2 ← phi( main/(byte*) main::sc#0 main::@1/(byte*) main::sc#1 ) + (byte*) main::camelot#2 ← phi( main/(byte*) main::camelot#0 main::@1/(byte*) main::camelot#1 ) + *((byte*) main::sc#2) ← *((byte*) main::camelot#2) + (byte*) main::sc#1 ← ++ (byte*) main::sc#2 + (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 + (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,6) + (bool~) main::$5 ← (byte) main::i#1 != rangelast(0,6) + if((bool~) main::$5) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + (byte*~) main::$0 ← (byte*) SCREEN#0 + (number) $28 + (byte*) main::sc2#0 ← (byte*~) main::$0 + (byte*) main::reigns#0 ← (const string) main::$7 + (byte) main::i1#0 ← (byte) 0 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@3 + (byte) main::i1#2 ← phi( main::@2/(byte) main::i1#0 main::@3/(byte) main::i1#1 ) + (byte*) main::sc2#2 ← phi( main::@2/(byte*) main::sc2#0 main::@3/(byte*) main::sc2#1 ) + (byte*) main::reigns#2 ← phi( main::@2/(byte*) main::reigns#0 main::@3/(byte*) main::reigns#1 ) + *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) + (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 + (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 + (byte) main::i1#1 ← (byte) main::i1#2 + rangenext(0,5) + (bool~) main::$6 ← (byte) main::i1#1 != rangelast(0,5) + if((bool~) main::$6) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@3 + (byte*~) main::$1 ← (byte*) SCREEN#0 + (number) $a + (void*) memcpy::destination#0 ← (void*)(byte*~) main::$1 + (void*) memcpy::source#0 ← (void*)(byte[]) CAMELOT#0 + (word) memcpy::num#0 ← (number) 7 + call memcpy + (void*) memcpy::return#2 ← (void*) memcpy::return#1 + to:main::@5 +main::@5: scope:[main] from main::@4 + (byte*~) main::$3 ← (byte*) SCREEN#0 + (number) $32 + (void*) memcpy::destination#1 ← (void*)(byte*~) main::$3 + (void*) memcpy::source#1 ← (void*)(const string) main::$8 + (word) memcpy::num#1 ← (number) 5 + call memcpy + (void*) memcpy::return#3 ← (void*) memcpy::return#1 + to:main::@6 +main::@6: scope:[main] from main::@5 + to:main::@return +main::@return: scope:[main] from main::@6 + return + to:@return +@5: scope:[] from @4 + call main + to:@6 +@6: scope:[] from @5 + to:@end +@end: scope:[] from @6 + +SYMBOL TABLE SSA +(const string) $0 = (string) "camelot" +(label) @4 +(label) @5 +(label) @6 +(label) @begin +(label) @end +(byte[]) CAMELOT +(byte[]) CAMELOT#0 +(byte*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(byte*~) main::$0 +(byte*~) main::$1 +(byte*~) main::$3 +(bool~) main::$5 +(bool~) main::$6 +(const string) main::$7 = (string) "reigns" +(const string) main::$8 = (string) "rules" +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return +(byte*) main::camelot +(byte*) main::camelot#0 +(byte*) main::camelot#1 +(byte*) main::camelot#2 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i1 +(byte) main::i1#0 +(byte) main::i1#1 +(byte) main::i1#2 +(byte*) main::reigns +(byte*) main::reigns#0 +(byte*) main::reigns#1 +(byte*) main::reigns#2 +(byte*) main::sc +(byte*) main::sc#0 +(byte*) main::sc#1 +(byte*) main::sc#2 +(byte*) main::sc2 +(byte*) main::sc2#0 +(byte*) main::sc2#1 +(byte*) main::sc2#2 +(void*()) memcpy((void*) memcpy::destination , (void*) memcpy::source , (word) memcpy::num) +(byte*~) memcpy::$0 +(byte*~) memcpy::$1 +(bool~) memcpy::$2 +(label) memcpy::@1 +(label) memcpy::@2 +(label) memcpy::@3 +(label) memcpy::@return +(void*) memcpy::destination +(void*) memcpy::destination#0 +(void*) memcpy::destination#1 +(void*) memcpy::destination#2 +(void*) memcpy::destination#3 +(void*) memcpy::destination#4 +(void*) memcpy::destination#5 +(byte*) memcpy::dst +(byte*) memcpy::dst#0 +(byte*) memcpy::dst#1 +(byte*) memcpy::dst#2 +(byte*) memcpy::dst#3 +(word) memcpy::num +(word) memcpy::num#0 +(word) memcpy::num#1 +(word) memcpy::num#2 +(void*) memcpy::return +(void*) memcpy::return#0 +(void*) memcpy::return#1 +(void*) memcpy::return#2 +(void*) memcpy::return#3 +(void*) memcpy::return#4 +(void*) memcpy::source +(void*) memcpy::source#0 +(void*) memcpy::source#1 +(void*) memcpy::source#2 +(byte*) memcpy::src +(byte*) memcpy::src#0 +(byte*) memcpy::src#1 +(byte*) memcpy::src#2 +(byte*) memcpy::src#3 +(byte*) memcpy::src_end +(byte*) memcpy::src_end#0 +(byte*) memcpy::src_end#1 +(byte*) memcpy::src_end#2 + +Adding number conversion cast (unumber) $28 in (byte*~) main::$0 ← (byte*) SCREEN#0 + (number) $28 +Adding number conversion cast (unumber) $a in (byte*~) main::$1 ← (byte*) SCREEN#0 + (number) $a +Adding number conversion cast (unumber) 7 in (word) memcpy::num#0 ← (number) 7 +Adding number conversion cast (unumber) $32 in (byte*~) main::$3 ← (byte*) SCREEN#0 + (number) $32 +Adding number conversion cast (unumber) 5 in (word) memcpy::num#1 ← (number) 5 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) memcpy::src#0 ← (byte*)(void*) memcpy::source#2 +Inlining cast (byte*) memcpy::dst#0 ← (byte*)(void*) memcpy::destination#2 +Inlining cast (byte*~) memcpy::$0 ← (byte*)(void*) memcpy::source#2 +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (word) memcpy::num#0 ← (unumber)(number) 7 +Inlining cast (word) memcpy::num#1 ← (unumber)(number) 5 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $28 +Simplifying constant integer cast $a +Simplifying constant integer cast 7 +Simplifying constant integer cast $32 +Simplifying constant integer cast 5 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $28 +Finalized unsigned number type (byte) $a +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) $32 +Finalized unsigned number type (byte) 5 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte*) memcpy::src_end#0 = (byte*~) memcpy::$1 +Alias (byte*) memcpy::src#2 = (byte*) memcpy::src#3 +Alias (byte*) memcpy::dst#2 = (byte*) memcpy::dst#3 +Alias (byte*) memcpy::src_end#1 = (byte*) memcpy::src_end#2 +Alias (void*) memcpy::destination#3 = (void*) memcpy::destination#5 (void*) memcpy::destination#4 (void*) memcpy::return#0 (void*) memcpy::return#4 (void*) memcpy::return#1 +Alias (byte*) main::sc2#0 = (byte*~) main::$0 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte*) memcpy::src_end#1 (byte*) memcpy::src_end#0 +Identical Phi Values (void*) memcpy::destination#3 (void*) memcpy::destination#2 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) memcpy::$2 [8] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 +Simple Condition (bool~) main::$5 [29] if((byte) main::i#1!=rangelast(0,6)) goto main::@1 +Simple Condition (bool~) main::$6 [40] if((byte) main::i1#1!=rangelast(0,5)) goto main::@3 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte[]) CAMELOT#0 = $0 +Constant (const byte) main::i#0 = 0 +Constant (const byte*) main::reigns#0 = main::$7 +Constant (const byte) main::i1#0 = 0 +Constant (const word) memcpy::num#0 = 7 +Constant (const void*) memcpy::source#1 = (void*)main::$8 +Constant (const word) memcpy::num#1 = 5 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte*) main::sc#0 = SCREEN#0 +Constant (const byte*) main::camelot#0 = CAMELOT#0 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (void*)CAMELOT#0 in [43] (void*) memcpy::source#0 ← (void*)(const byte[]) CAMELOT#0 +Successful SSA optimization Pass2ConstantValues +Resolved ranged next value [27] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [29] if(main::i#1!=rangelast(0,6)) goto main::@1 to (number) 7 +Resolved ranged next value [38] main::i1#1 ← ++ main::i1#2 to ++ +Resolved ranged comparison value [40] if(main::i1#1!=rangelast(0,5)) goto main::@3 to (number) 6 +Eliminating unused variable (void*) memcpy::return#2 and assignment [28] (void*) memcpy::return#2 ← (void*) memcpy::destination#2 +Eliminating unused variable (void*) memcpy::return#3 and assignment [32] (void*) memcpy::return#3 ← (void*) memcpy::destination#2 +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 7 in if((byte) main::i#1!=(number) 7) goto main::@1 +Adding number conversion cast (unumber) 6 in if((byte) main::i1#1!=(number) 6) goto main::@3 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 7 +Simplifying constant integer cast 6 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 6 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [17] (byte*) main::sc2#0 ← (const byte*) SCREEN#0 + (byte) $28 +Constant right-side identified [24] (byte*~) main::$1 ← (const byte*) SCREEN#0 + (byte) $a +Constant right-side identified [28] (byte*~) main::$3 ← (const byte*) SCREEN#0 + (byte) $32 +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::sc2#0 = SCREEN#0+$28 +Constant (const byte*) main::$1 = SCREEN#0+$a +Constant (const void*) memcpy::source#0 = (void*)CAMELOT#0 +Constant (const byte*) main::$3 = SCREEN#0+$32 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (void*)main::$1 in [25] (void*) memcpy::destination#0 ← (void*)(const byte*) main::$1 +Constant value identified (void*)main::$3 in [29] (void*) memcpy::destination#1 ← (void*)(const byte*) main::$3 +Successful SSA optimization Pass2ConstantValues +Constant (const void*) memcpy::destination#0 = (void*)main::$1 +Constant (const void*) memcpy::destination#1 = (void*)main::$3 +Successful SSA optimization Pass2ConstantIdentification +Inlining Noop Cast [1] (byte*) memcpy::src#0 ← (byte*)(void*) memcpy::source#2 keeping memcpy::source#2 +Inlining Noop Cast [2] (byte*) memcpy::dst#0 ← (byte*)(void*) memcpy::destination#2 keeping memcpy::destination#2 +Inlining Noop Cast [3] (byte*~) memcpy::$0 ← (byte*)(void*) memcpy::source#2 keeping memcpy::source#2 +Successful SSA optimization Pass2NopCastInlining +Inlining constant with var siblings (const word) memcpy::num#0 +Inlining constant with var siblings (const void*) memcpy::source#1 +Inlining constant with var siblings (const word) memcpy::num#1 +Inlining constant with var siblings (const void*) memcpy::source#0 +Inlining constant with var siblings (const void*) memcpy::destination#0 +Inlining constant with var siblings (const void*) memcpy::destination#1 +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte*) main::reigns#0 +Inlining constant with var siblings (const byte) main::i1#0 +Inlining constant with var siblings (const byte*) main::sc#0 +Inlining constant with var siblings (const byte*) main::camelot#0 +Inlining constant with var siblings (const byte*) main::sc2#0 +Constant inlined main::camelot#0 = (const byte[]) CAMELOT#0 +Constant inlined main::sc#0 = (const byte*) SCREEN#0 +Constant inlined memcpy::destination#0 = (void*)(const byte*) SCREEN#0+(byte) $a +Constant inlined memcpy::destination#1 = (void*)(const byte*) SCREEN#0+(byte) $32 +Constant inlined $0 = (const byte[]) CAMELOT#0 +Constant inlined memcpy::source#0 = (void*)(const byte[]) CAMELOT#0 +Constant inlined memcpy::num#1 = (byte) 5 +Constant inlined memcpy::num#0 = (byte) 7 +Constant inlined memcpy::source#1 = (void*)(const string) main::$8 +Constant inlined main::$1 = (const byte*) SCREEN#0+(byte) $a +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::i1#0 = (byte) 0 +Constant inlined main::$3 = (const byte*) SCREEN#0+(byte) $32 +Constant inlined main::sc2#0 = (const byte*) SCREEN#0+(byte) $28 +Constant inlined main::$7 = (const byte*) main::reigns#0 +Successful SSA optimization Pass2ConstantInlining +Inlining constant with var siblings (const byte*) main::reigns#0 +Inlining constant with var siblings (const byte*) main::reigns#0 +Added new block during phi lifting main::@7(between main::@1 and main::@1) +Added new block during phi lifting main::@8(between main::@3 and main::@3) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @4 +Adding NOP phi() at start of @5 +Adding NOP phi() at start of @6 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of main::@4 +Adding NOP phi() at start of main::@5 +Adding NOP phi() at start of main::@6 +Adding NOP phi() at start of memcpy::@3 +CALL GRAPH +Calls in [] to main:3 +Calls in [main] to memcpy:21 memcpy:23 + +Created 11 initial phi equivalence classes +Coalesced [26] main::reigns#3 ← main::reigns#1 +Coalesced [27] main::sc2#3 ← main::sc2#1 +Coalesced [28] main::i1#3 ← main::i1#1 +Coalesced [29] main::camelot#3 ← main::camelot#1 +Coalesced [30] main::sc#3 ← main::sc#1 +Coalesced [31] main::i#3 ← main::i#1 +Coalesced [43] memcpy::src#5 ← memcpy::src#1 +Coalesced [44] memcpy::dst#5 ← memcpy::dst#1 +Coalesced down to 11 phi equivalence classes +Culled Empty Block (label) @4 +Culled Empty Block (label) @6 +Culled Empty Block (label) main::@2 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) main::@8 +Culled Empty Block (label) main::@7 +Culled Empty Block (label) memcpy::@3 +Renumbering block @5 to @1 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +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 +Adding NOP phi() at start of main::@3 +Adding NOP phi() at start of main::@4 + +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::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 ) + [5] (byte*) main::sc#2 ← phi( main/(const byte*) SCREEN#0 main::@1/(byte*) main::sc#1 ) + [5] (byte*) main::camelot#2 ← phi( main/(const byte[]) CAMELOT#0 main::@1/(byte*) main::camelot#1 ) + [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) + [7] (byte*) main::sc#1 ← ++ (byte*) main::sc#2 + [8] (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte) 7) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [11] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [11] (byte*) main::sc2#2 ← phi( main::@1/(const byte*) SCREEN#0+(byte) $28 main::@2/(byte*) main::sc2#1 ) + [11] (byte*) main::reigns#2 ← phi( main::@1/(const byte*) main::reigns#0 main::@2/(byte*) main::reigns#1 ) + [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) + [13] (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 + [14] (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 + [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [16] if((byte) main::i1#1!=(byte) 6) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [17] phi() + [18] call memcpy + to:main::@4 +main::@4: scope:[main] from main::@3 + [19] phi() + [20] call memcpy + to:main::@return +main::@return: scope:[main] from main::@4 + [21] return + to:@return +memcpy: scope:[memcpy] from main::@3 main::@4 + [22] (word) memcpy::num#2 ← phi( main::@3/(byte) 7 main::@4/(byte) 5 ) + [22] (void*) memcpy::destination#2 ← phi( main::@3/(void*)(const byte*) SCREEN#0+(byte) $a main::@4/(void*)(const byte*) SCREEN#0+(byte) $32 ) + [22] (void*) memcpy::source#2 ← phi( main::@3/(void*)(const byte[]) CAMELOT#0 main::@4/(void*)(const string) main::$8 ) + [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 + [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 + [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 + to:memcpy::@1 +memcpy::@1: scope:[memcpy] from memcpy memcpy::@2 + [26] (byte*) memcpy::dst#2 ← phi( memcpy/(byte*~) memcpy::dst#4 memcpy::@2/(byte*) memcpy::dst#1 ) + [26] (byte*) memcpy::src#2 ← phi( memcpy/(byte*~) memcpy::src#4 memcpy::@2/(byte*) memcpy::src#1 ) + [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 + to:memcpy::@return +memcpy::@return: scope:[memcpy] from memcpy::@1 + [28] return + to:@return +memcpy::@2: scope:[memcpy] from memcpy::@1 + [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) + [30] (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 + [31] (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#2 + to:memcpy::@1 + + +VARIABLE REGISTER WEIGHTS +(byte[]) CAMELOT +(byte*) SCREEN +(void()) main() +(byte*) main::camelot +(byte*) main::camelot#1 7.333333333333333 +(byte*) main::camelot#2 11.0 +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 5.5 +(byte) main::i1 +(byte) main::i1#1 16.5 +(byte) main::i1#2 5.5 +(byte*) main::reigns +(byte*) main::reigns#1 7.333333333333333 +(byte*) main::reigns#2 11.0 +(byte*) main::sc +(byte*) main::sc#1 5.5 +(byte*) main::sc#2 16.5 +(byte*) main::sc2 +(byte*) main::sc2#1 5.5 +(byte*) main::sc2#2 16.5 +(void*()) memcpy((void*) memcpy::destination , (void*) memcpy::source , (word) memcpy::num) +(void*) memcpy::destination +(void*) memcpy::destination#2 +(byte*) memcpy::dst +(byte*) memcpy::dst#1 11.0 +(byte*) memcpy::dst#2 11.666666666666666 +(byte*~) memcpy::dst#4 4.0 +(word) memcpy::num +(word) memcpy::num#2 2.0 +(void*) memcpy::return +(void*) memcpy::source +(void*) memcpy::source#2 +(byte*) memcpy::src +(byte*) memcpy::src#1 22.0 +(byte*) memcpy::src#2 11.5 +(byte*~) memcpy::src#4 2.0 +(byte*) memcpy::src_end +(byte*) memcpy::src_end#0 1.625 + +Initial phi equivalence classes +[ main::camelot#2 main::camelot#1 ] +[ main::sc#2 main::sc#1 ] +[ main::i#2 main::i#1 ] +[ main::reigns#2 main::reigns#1 ] +[ main::sc2#2 main::sc2#1 ] +[ main::i1#2 main::i1#1 ] +[ memcpy::source#2 ] +[ memcpy::destination#2 ] +[ memcpy::num#2 ] +[ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] +[ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] +Added variable memcpy::src_end#0 to zero page equivalence class [ memcpy::src_end#0 ] +Complete equivalence classes +[ main::camelot#2 main::camelot#1 ] +[ main::sc#2 main::sc#1 ] +[ main::i#2 main::i#1 ] +[ main::reigns#2 main::reigns#1 ] +[ main::sc2#2 main::sc2#1 ] +[ main::i1#2 main::i1#1 ] +[ memcpy::source#2 ] +[ memcpy::destination#2 ] +[ memcpy::num#2 ] +[ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] +[ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] +[ memcpy::src_end#0 ] +Allocated zp ZP_WORD:2 [ main::camelot#2 main::camelot#1 ] +Allocated zp ZP_WORD:4 [ main::sc#2 main::sc#1 ] +Allocated zp ZP_BYTE:6 [ main::i#2 main::i#1 ] +Allocated zp ZP_WORD:7 [ main::reigns#2 main::reigns#1 ] +Allocated zp ZP_WORD:9 [ main::sc2#2 main::sc2#1 ] +Allocated zp ZP_BYTE:11 [ main::i1#2 main::i1#1 ] +Allocated zp ZP_WORD:12 [ memcpy::source#2 ] +Allocated zp ZP_WORD:14 [ memcpy::destination#2 ] +Allocated zp ZP_WORD:16 [ memcpy::num#2 ] +Allocated zp ZP_WORD:18 [ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] +Allocated zp ZP_WORD:20 [ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] +Allocated zp ZP_WORD:22 [ memcpy::src_end#0 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test memcpy on strings ( + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [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 sc = 4 + .label camelot = 2 + .label i = 6 + .label sc2 = 9 + .label reigns = 7 + .label i1 = $b + // [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 + // [5] phi (byte*) main::sc#2 = (const byte*) SCREEN#0 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #SCREEN + sta.z sc+1 + // [5] phi (byte*) main::camelot#2 = (const byte[]) CAMELOT#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #CAMELOT + sta.z camelot+1 + jmp b1 + // [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + // [5] phi (byte*) main::sc#2 = (byte*) main::sc#1 [phi:main::@1->main::@1#1] -- register_copy + // [5] phi (byte*) main::camelot#2 = (byte*) main::camelot#1 [phi:main::@1->main::@1#2] -- register_copy + jmp b1 + // main::@1 + b1: + // [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (camelot),y + ldy #0 + sta (sc),y + // [7] (byte*) main::sc#1 ← ++ (byte*) main::sc#2 -- pbuz1=_inc_pbuz1 + inc.z sc + bne !+ + inc.z sc+1 + !: + // [8] (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 -- pbuz1=_inc_pbuz1 + inc.z camelot + bne !+ + inc.z camelot+1 + !: + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [10] if((byte) main::i#1!=(byte) 7) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #7 + cmp.z i + bne b1_from_b1 + // [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta.z i1 + // [11] phi (byte*) main::sc2#2 = (const byte*) SCREEN#0+(byte) $28 [phi:main::@1->main::@2#1] -- pbuz1=pbuc1 + lda #SCREEN+$28 + sta.z sc2+1 + // [11] phi (byte*) main::reigns#2 = (const byte*) main::reigns#0 [phi:main::@1->main::@2#2] -- pbuz1=pbuc1 + lda #reigns_0 + sta.z reigns+1 + jmp b2 + // [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + // [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + // [11] phi (byte*) main::sc2#2 = (byte*) main::sc2#1 [phi:main::@2->main::@2#1] -- register_copy + // [11] phi (byte*) main::reigns#2 = (byte*) main::reigns#1 [phi:main::@2->main::@2#2] -- register_copy + jmp b2 + // main::@2 + b2: + // [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (reigns),y + ldy #0 + sta (sc2),y + // [13] (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 -- pbuz1=_inc_pbuz1 + inc.z sc2 + bne !+ + inc.z sc2+1 + !: + // [14] (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 -- pbuz1=_inc_pbuz1 + inc.z reigns + bne !+ + inc.z reigns+1 + !: + // [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc.z i1 + // [16] if((byte) main::i1#1!=(byte) 6) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i1 + bne b2_from_b2 + // [17] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + b3_from_b2: + jmp b3 + // main::@3 + b3: + // [18] call memcpy + // [22] phi from main::@3 to memcpy [phi:main::@3->memcpy] + memcpy_from_b3: + // [22] phi (word) memcpy::num#2 = (byte) 7 [phi:main::@3->memcpy#0] -- vwuz1=vbuc1 + lda #<7 + sta.z memcpy.num + lda #>7 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $a [phi:main::@3->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$a + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const byte[]) CAMELOT#0 [phi:main::@3->memcpy#2] -- pvoz1=pvoc1 + lda #CAMELOT + sta.z memcpy.source+1 + jsr memcpy + // [19] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + b4_from_b3: + jmp b4 + // main::@4 + b4: + // [20] call memcpy + // [22] phi from main::@4 to memcpy [phi:main::@4->memcpy] + memcpy_from_b4: + // [22] phi (word) memcpy::num#2 = (byte) 5 [phi:main::@4->memcpy#0] -- vwuz1=vbuc1 + lda #<5 + sta.z memcpy.num + lda #>5 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $32 [phi:main::@4->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$32 + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const string) main::$8 [phi:main::@4->memcpy#2] -- pvoz1=pvoc1 + lda #<_8 + sta.z memcpy.source + lda #>_8 + sta.z memcpy.source+1 + jsr memcpy + jmp breturn + // main::@return + breturn: + // [21] return + rts + _8: .text "rules" + .byte 0 + reigns_0: .text "reigns" + .byte 0 +} + // memcpy +// Copy block of memory (forwards) +// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. +// memcpy(void* zeropage($e) destination, void* zeropage($c) source, word zeropage($10) num) +memcpy: { + .label src_end = $16 + .label dst = $14 + .label src = $12 + .label source = $c + .label destination = $e + .label num = $10 + // [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 -- pbuz1=pbuz2_plus_vwuz3 + lda.z source + clc + adc.z num + sta.z src_end + lda.z source+1 + adc.z num+1 + sta.z src_end+1 + // [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 -- pbuz1=pbuz2 + lda.z source + sta.z src + lda.z source+1 + sta.z src+1 + // [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 -- pbuz1=pbuz2 + lda.z destination + sta.z dst + lda.z destination+1 + sta.z dst+1 + // [26] phi from memcpy memcpy::@2 to memcpy::@1 [phi:memcpy/memcpy::@2->memcpy::@1] + b1_from_memcpy: + b1_from_b2: + // [26] phi (byte*) memcpy::dst#2 = (byte*~) memcpy::dst#4 [phi:memcpy/memcpy::@2->memcpy::@1#0] -- register_copy + // [26] phi (byte*) memcpy::src#2 = (byte*~) memcpy::src#4 [phi:memcpy/memcpy::@2->memcpy::@1#1] -- register_copy + jmp b1 + // memcpy::@1 + b1: + // [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 -- pbuz1_neq_pbuz2_then_la1 + lda.z src+1 + cmp.z src_end+1 + bne b2 + lda.z src + cmp.z src_end + bne b2 + jmp breturn + // memcpy::@return + breturn: + // [28] return + rts + // memcpy::@2 + b2: + // [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + ldy #0 + sta (dst),y + // [30] (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [31] (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#2 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + jmp b1_from_b2 +} + // File Data + CAMELOT: .text "camelot" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) [ main::camelot#2 main::sc#2 main::i#2 ] ( main:2 [ main::camelot#2 main::sc#2 main::i#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:6 [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:6 [ main::i#2 main::i#1 ] +Statement [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) [ main::reigns#2 main::sc2#2 main::i1#2 ] ( main:2 [ main::reigns#2 main::sc2#2 main::i1#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:11 [ main::i1#2 main::i1#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:11 [ main::i1#2 main::i1#1 ] +Statement [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] ( main:2::memcpy:18 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] main:2::memcpy:20 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] ) always clobbers reg byte a +Statement [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] ( main:2::memcpy:18 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] main:2::memcpy:20 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] ) always clobbers reg byte a +Statement [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] ) always clobbers reg byte a +Statement [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ) always clobbers reg byte a +Statement [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ) always clobbers reg byte a reg byte y +Statement [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) [ main::camelot#2 main::sc#2 main::i#2 ] ( main:2 [ main::camelot#2 main::sc#2 main::i#2 ] ) always clobbers reg byte a reg byte y +Statement [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) [ main::reigns#2 main::sc2#2 main::i1#2 ] ( main:2 [ main::reigns#2 main::sc2#2 main::i1#2 ] ) always clobbers reg byte a reg byte y +Statement [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] ( main:2::memcpy:18 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] main:2::memcpy:20 [ memcpy::source#2 memcpy::destination#2 memcpy::src_end#0 ] ) always clobbers reg byte a +Statement [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] ( main:2::memcpy:18 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] main:2::memcpy:20 [ memcpy::destination#2 memcpy::src_end#0 memcpy::src#4 ] ) always clobbers reg byte a +Statement [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#4 memcpy::dst#4 ] ) always clobbers reg byte a +Statement [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ) always clobbers reg byte a +Statement [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ( main:2::memcpy:18 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] main:2::memcpy:20 [ memcpy::src_end#0 memcpy::src#2 memcpy::dst#2 ] ) always clobbers reg byte a reg byte y +Potential registers zp ZP_WORD:2 [ main::camelot#2 main::camelot#1 ] : zp ZP_WORD:2 , +Potential registers zp ZP_WORD:4 [ main::sc#2 main::sc#1 ] : zp ZP_WORD:4 , +Potential registers zp ZP_BYTE:6 [ main::i#2 main::i#1 ] : zp ZP_BYTE:6 , reg byte x , +Potential registers zp ZP_WORD:7 [ main::reigns#2 main::reigns#1 ] : zp ZP_WORD:7 , +Potential registers zp ZP_WORD:9 [ main::sc2#2 main::sc2#1 ] : zp ZP_WORD:9 , +Potential registers zp ZP_BYTE:11 [ main::i1#2 main::i1#1 ] : zp ZP_BYTE:11 , reg byte x , +Potential registers zp ZP_WORD:12 [ memcpy::source#2 ] : zp ZP_WORD:12 , +Potential registers zp ZP_WORD:14 [ memcpy::destination#2 ] : zp ZP_WORD:14 , +Potential registers zp ZP_WORD:16 [ memcpy::num#2 ] : zp ZP_WORD:16 , +Potential registers zp ZP_WORD:18 [ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] : zp ZP_WORD:18 , +Potential registers zp ZP_WORD:20 [ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] : zp ZP_WORD:20 , +Potential registers zp ZP_WORD:22 [ memcpy::src_end#0 ] : zp ZP_WORD:22 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 22: zp ZP_WORD:4 [ main::sc#2 main::sc#1 ] 22: zp ZP_BYTE:6 [ main::i#2 main::i#1 ] 22: zp ZP_WORD:9 [ main::sc2#2 main::sc2#1 ] 22: zp ZP_BYTE:11 [ main::i1#2 main::i1#1 ] 18.33: zp ZP_WORD:2 [ main::camelot#2 main::camelot#1 ] 18.33: zp ZP_WORD:7 [ main::reigns#2 main::reigns#1 ] +Uplift Scope [memcpy] 35.5: zp ZP_WORD:18 [ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] 26.67: zp ZP_WORD:20 [ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] 2: zp ZP_WORD:16 [ memcpy::num#2 ] 1.62: zp ZP_WORD:22 [ memcpy::src_end#0 ] 0: zp ZP_WORD:12 [ memcpy::source#2 ] 0: zp ZP_WORD:14 [ memcpy::destination#2 ] +Uplift Scope [] + +Uplifting [main] best 2314 combination zp ZP_WORD:4 [ main::sc#2 main::sc#1 ] reg byte x [ main::i#2 main::i#1 ] zp ZP_WORD:9 [ main::sc2#2 main::sc2#1 ] reg byte x [ main::i1#2 main::i1#1 ] zp ZP_WORD:2 [ main::camelot#2 main::camelot#1 ] zp ZP_WORD:7 [ main::reigns#2 main::reigns#1 ] +Uplifting [memcpy] best 2314 combination zp ZP_WORD:18 [ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] zp ZP_WORD:20 [ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] zp ZP_WORD:16 [ memcpy::num#2 ] zp ZP_WORD:22 [ memcpy::src_end#0 ] zp ZP_WORD:12 [ memcpy::source#2 ] zp ZP_WORD:14 [ memcpy::destination#2 ] +Uplifting [] best 2314 combination +Coalescing zero page register [ zp ZP_WORD:12 [ memcpy::source#2 ] ] with [ zp ZP_WORD:18 [ memcpy::src#2 memcpy::src#4 memcpy::src#1 ] ] - score: 1 +Coalescing zero page register [ zp ZP_WORD:14 [ memcpy::destination#2 ] ] with [ zp ZP_WORD:20 [ memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] ] - score: 1 +Coalescing zero page register [ zp ZP_WORD:16 [ memcpy::num#2 ] ] with [ zp ZP_WORD:22 [ memcpy::src_end#0 ] ] - score: 1 +Coalescing zero page register [ zp ZP_WORD:12 [ memcpy::source#2 memcpy::src#2 memcpy::src#4 memcpy::src#1 ] ] with [ zp ZP_WORD:2 [ main::camelot#2 main::camelot#1 ] ] +Coalescing zero page register [ zp ZP_WORD:14 [ memcpy::destination#2 memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 ] ] with [ zp ZP_WORD:4 [ main::sc#2 main::sc#1 ] ] +Coalescing zero page register [ zp ZP_WORD:16 [ memcpy::num#2 memcpy::src_end#0 ] ] with [ zp ZP_WORD:7 [ main::reigns#2 main::reigns#1 ] ] +Allocated (was zp ZP_WORD:9) zp ZP_WORD:2 [ main::sc2#2 main::sc2#1 ] +Allocated (was zp ZP_WORD:12) zp ZP_WORD:4 [ memcpy::source#2 memcpy::src#2 memcpy::src#4 memcpy::src#1 main::camelot#2 main::camelot#1 ] +Allocated (was zp ZP_WORD:14) zp ZP_WORD:6 [ memcpy::destination#2 memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 main::sc#2 main::sc#1 ] +Allocated (was zp ZP_WORD:16) zp ZP_WORD:8 [ memcpy::num#2 memcpy::src_end#0 main::reigns#2 main::reigns#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test memcpy on strings ( + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [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 sc = 6 + .label camelot = 4 + .label sc2 = 2 + .label reigns = 8 + // [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 + // [5] phi (byte*) main::sc#2 = (const byte*) SCREEN#0 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #SCREEN + sta.z sc+1 + // [5] phi (byte*) main::camelot#2 = (const byte[]) CAMELOT#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #CAMELOT + sta.z camelot+1 + jmp b1 + // [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + // [5] phi (byte*) main::sc#2 = (byte*) main::sc#1 [phi:main::@1->main::@1#1] -- register_copy + // [5] phi (byte*) main::camelot#2 = (byte*) main::camelot#1 [phi:main::@1->main::@1#2] -- register_copy + jmp b1 + // main::@1 + b1: + // [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (camelot),y + ldy #0 + sta (sc),y + // [7] (byte*) main::sc#1 ← ++ (byte*) main::sc#2 -- pbuz1=_inc_pbuz1 + inc.z sc + bne !+ + inc.z sc+1 + !: + // [8] (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 -- pbuz1=_inc_pbuz1 + inc.z camelot + bne !+ + inc.z camelot+1 + !: + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [10] if((byte) main::i#1!=(byte) 7) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #7 + bne b1_from_b1 + // [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + // [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + // [11] phi (byte*) main::sc2#2 = (const byte*) SCREEN#0+(byte) $28 [phi:main::@1->main::@2#1] -- pbuz1=pbuc1 + lda #SCREEN+$28 + sta.z sc2+1 + // [11] phi (byte*) main::reigns#2 = (const byte*) main::reigns#0 [phi:main::@1->main::@2#2] -- pbuz1=pbuc1 + lda #reigns_0 + sta.z reigns+1 + jmp b2 + // [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + // [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + // [11] phi (byte*) main::sc2#2 = (byte*) main::sc2#1 [phi:main::@2->main::@2#1] -- register_copy + // [11] phi (byte*) main::reigns#2 = (byte*) main::reigns#1 [phi:main::@2->main::@2#2] -- register_copy + jmp b2 + // main::@2 + b2: + // [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (reigns),y + ldy #0 + sta (sc2),y + // [13] (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 -- pbuz1=_inc_pbuz1 + inc.z sc2 + bne !+ + inc.z sc2+1 + !: + // [14] (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 -- pbuz1=_inc_pbuz1 + inc.z reigns + bne !+ + inc.z reigns+1 + !: + // [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuxx=_inc_vbuxx + inx + // [16] if((byte) main::i1#1!=(byte) 6) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne b2_from_b2 + // [17] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + b3_from_b2: + jmp b3 + // main::@3 + b3: + // [18] call memcpy + // [22] phi from main::@3 to memcpy [phi:main::@3->memcpy] + memcpy_from_b3: + // [22] phi (word) memcpy::num#2 = (byte) 7 [phi:main::@3->memcpy#0] -- vwuz1=vbuc1 + lda #<7 + sta.z memcpy.num + lda #>7 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $a [phi:main::@3->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$a + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const byte[]) CAMELOT#0 [phi:main::@3->memcpy#2] -- pvoz1=pvoc1 + lda #CAMELOT + sta.z memcpy.source+1 + jsr memcpy + // [19] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + b4_from_b3: + jmp b4 + // main::@4 + b4: + // [20] call memcpy + // [22] phi from main::@4 to memcpy [phi:main::@4->memcpy] + memcpy_from_b4: + // [22] phi (word) memcpy::num#2 = (byte) 5 [phi:main::@4->memcpy#0] -- vwuz1=vbuc1 + lda #<5 + sta.z memcpy.num + lda #>5 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $32 [phi:main::@4->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$32 + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const string) main::$8 [phi:main::@4->memcpy#2] -- pvoz1=pvoc1 + lda #<_8 + sta.z memcpy.source + lda #>_8 + sta.z memcpy.source+1 + jsr memcpy + jmp breturn + // main::@return + breturn: + // [21] return + rts + _8: .text "rules" + .byte 0 + reigns_0: .text "reigns" + .byte 0 +} + // memcpy +// Copy block of memory (forwards) +// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. +// memcpy(void* zeropage(6) destination, void* zeropage(4) source, word zeropage(8) num) +memcpy: { + .label src_end = 8 + .label dst = 6 + .label src = 4 + .label source = 4 + .label destination = 6 + .label num = 8 + // [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 -- pbuz1=pbuz2_plus_vwuz1 + lda.z src_end + clc + adc.z source + sta.z src_end + lda.z src_end+1 + adc.z source+1 + sta.z src_end+1 + // [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 + // [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 + // [26] phi from memcpy memcpy::@2 to memcpy::@1 [phi:memcpy/memcpy::@2->memcpy::@1] + b1_from_memcpy: + b1_from_b2: + // [26] phi (byte*) memcpy::dst#2 = (byte*~) memcpy::dst#4 [phi:memcpy/memcpy::@2->memcpy::@1#0] -- register_copy + // [26] phi (byte*) memcpy::src#2 = (byte*~) memcpy::src#4 [phi:memcpy/memcpy::@2->memcpy::@1#1] -- register_copy + jmp b1 + // memcpy::@1 + b1: + // [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 -- pbuz1_neq_pbuz2_then_la1 + lda.z src+1 + cmp.z src_end+1 + bne b2 + lda.z src + cmp.z src_end + bne b2 + jmp breturn + // memcpy::@return + breturn: + // [28] return + rts + // memcpy::@2 + b2: + // [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + ldy #0 + sta (dst),y + // [30] (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [31] (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#2 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + jmp b1_from_b2 +} + // File Data + CAMELOT: .text "camelot" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp b3 +Removing instruction jmp b4 +Removing instruction jmp breturn +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction ldy #0 +Removing instruction ldy #0 +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b1_from_b1 with b1 +Replacing label b2_from_b2 with b2 +Replacing label b1_from_b2 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b1: +Removing instruction b2_from_b2: +Removing instruction b3_from_b2: +Removing instruction memcpy_from_b3: +Removing instruction b4_from_b3: +Removing instruction memcpy_from_b4: +Removing instruction b1_from_memcpy: +Removing instruction b1_from_b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b2_from_b1: +Removing instruction b3: +Removing instruction b4: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +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[]) CAMELOT +(const byte[]) CAMELOT#0 CAMELOT = (string) "camelot" +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(const string) main::$8 $8 = (string) "rules" +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte*) main::camelot +(byte*) main::camelot#1 camelot zp ZP_WORD:4 7.333333333333333 +(byte*) main::camelot#2 camelot zp ZP_WORD:4 11.0 +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 5.5 +(byte) main::i1 +(byte) main::i1#1 reg byte x 16.5 +(byte) main::i1#2 reg byte x 5.5 +(byte*) main::reigns +(const byte*) main::reigns#0 reigns#0 = (string) "reigns" +(byte*) main::reigns#1 reigns zp ZP_WORD:8 7.333333333333333 +(byte*) main::reigns#2 reigns zp ZP_WORD:8 11.0 +(byte*) main::sc +(byte*) main::sc#1 sc zp ZP_WORD:6 5.5 +(byte*) main::sc#2 sc zp ZP_WORD:6 16.5 +(byte*) main::sc2 +(byte*) main::sc2#1 sc2 zp ZP_WORD:2 5.5 +(byte*) main::sc2#2 sc2 zp ZP_WORD:2 16.5 +(void*()) memcpy((void*) memcpy::destination , (void*) memcpy::source , (word) memcpy::num) +(label) memcpy::@1 +(label) memcpy::@2 +(label) memcpy::@return +(void*) memcpy::destination +(void*) memcpy::destination#2 destination zp ZP_WORD:6 +(byte*) memcpy::dst +(byte*) memcpy::dst#1 dst zp ZP_WORD:6 11.0 +(byte*) memcpy::dst#2 dst zp ZP_WORD:6 11.666666666666666 +(byte*~) memcpy::dst#4 dst zp ZP_WORD:6 4.0 +(word) memcpy::num +(word) memcpy::num#2 num zp ZP_WORD:8 2.0 +(void*) memcpy::return +(void*) memcpy::source +(void*) memcpy::source#2 source zp ZP_WORD:4 +(byte*) memcpy::src +(byte*) memcpy::src#1 src zp ZP_WORD:4 22.0 +(byte*) memcpy::src#2 src zp ZP_WORD:4 11.5 +(byte*~) memcpy::src#4 src zp ZP_WORD:4 2.0 +(byte*) memcpy::src_end +(byte*) memcpy::src_end#0 src_end zp ZP_WORD:8 1.625 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::sc2#2 main::sc2#1 ] +reg byte x [ main::i1#2 main::i1#1 ] +zp ZP_WORD:4 [ memcpy::source#2 memcpy::src#2 memcpy::src#4 memcpy::src#1 main::camelot#2 main::camelot#1 ] +zp ZP_WORD:6 [ memcpy::destination#2 memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 main::sc#2 main::sc#1 ] +zp ZP_WORD:8 [ memcpy::num#2 memcpy::src_end#0 main::reigns#2 main::reigns#1 ] + + +FINAL ASSEMBLER +Score: 2029 + + // File Comments +// Test memcpy on strings ( + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label sc = 6 + .label camelot = 4 + .label sc2 = 2 + .label reigns = 8 + // [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 + // [5] phi (byte*) main::sc#2 = (const byte*) SCREEN#0 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #SCREEN + sta.z sc+1 + // [5] phi (byte*) main::camelot#2 = (const byte[]) CAMELOT#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #CAMELOT + sta.z camelot+1 + // [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + // [5] phi (byte*) main::sc#2 = (byte*) main::sc#1 [phi:main::@1->main::@1#1] -- register_copy + // [5] phi (byte*) main::camelot#2 = (byte*) main::camelot#1 [phi:main::@1->main::@1#2] -- register_copy + // main::@1 + b1: + // *sc++ = *camelot++ + // [6] *((byte*) main::sc#2) ← *((byte*) main::camelot#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (camelot),y + sta (sc),y + // *sc++ = *camelot++; + // [7] (byte*) main::sc#1 ← ++ (byte*) main::sc#2 -- pbuz1=_inc_pbuz1 + inc.z sc + bne !+ + inc.z sc+1 + !: + // [8] (byte*) main::camelot#1 ← ++ (byte*) main::camelot#2 -- pbuz1=_inc_pbuz1 + inc.z camelot + bne !+ + inc.z camelot+1 + !: + // for( char i: 0..6) + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [10] if((byte) main::i#1!=(byte) 7) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #7 + bne b1 + // [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + // [11] phi (byte*) main::sc2#2 = (const byte*) SCREEN#0+(byte) $28 [phi:main::@1->main::@2#1] -- pbuz1=pbuc1 + lda #SCREEN+$28 + sta.z sc2+1 + // [11] phi (byte*) main::reigns#2 = (const byte*) main::reigns#0 [phi:main::@1->main::@2#2] -- pbuz1=pbuc1 + lda #reigns_0 + sta.z reigns+1 + // [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + // [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + // [11] phi (byte*) main::sc2#2 = (byte*) main::sc2#1 [phi:main::@2->main::@2#1] -- register_copy + // [11] phi (byte*) main::reigns#2 = (byte*) main::reigns#1 [phi:main::@2->main::@2#2] -- register_copy + // main::@2 + b2: + // *sc2++ = *reigns++ + // [12] *((byte*) main::sc2#2) ← *((byte*) main::reigns#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (reigns),y + sta (sc2),y + // *sc2++ = *reigns++; + // [13] (byte*) main::sc2#1 ← ++ (byte*) main::sc2#2 -- pbuz1=_inc_pbuz1 + inc.z sc2 + bne !+ + inc.z sc2+1 + !: + // [14] (byte*) main::reigns#1 ← ++ (byte*) main::reigns#2 -- pbuz1=_inc_pbuz1 + inc.z reigns + bne !+ + inc.z reigns+1 + !: + // for( char i: 0..5) + // [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuxx=_inc_vbuxx + inx + // [16] if((byte) main::i1#1!=(byte) 6) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne b2 + // [17] phi from main::@2 to main::@3 [phi:main::@2->main::@3] + // main::@3 + // memcpy(SCREEN+10, CAMELOT, 7) + // [18] call memcpy + // [22] phi from main::@3 to memcpy [phi:main::@3->memcpy] + // [22] phi (word) memcpy::num#2 = (byte) 7 [phi:main::@3->memcpy#0] -- vwuz1=vbuc1 + lda #<7 + sta.z memcpy.num + lda #>7 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $a [phi:main::@3->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$a + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const byte[]) CAMELOT#0 [phi:main::@3->memcpy#2] -- pvoz1=pvoc1 + lda #CAMELOT + sta.z memcpy.source+1 + jsr memcpy + // [19] phi from main::@3 to main::@4 [phi:main::@3->main::@4] + // main::@4 + // memcpy(SCREEN+50, "rules", 5) + // [20] call memcpy + // [22] phi from main::@4 to memcpy [phi:main::@4->memcpy] + // [22] phi (word) memcpy::num#2 = (byte) 5 [phi:main::@4->memcpy#0] -- vwuz1=vbuc1 + lda #<5 + sta.z memcpy.num + lda #>5 + sta.z memcpy.num+1 + // [22] phi (void*) memcpy::destination#2 = (void*)(const byte*) SCREEN#0+(byte) $32 [phi:main::@4->memcpy#1] -- pvoz1=pvoc1 + lda #SCREEN+$32 + sta.z memcpy.destination+1 + // [22] phi (void*) memcpy::source#2 = (void*)(const string) main::$8 [phi:main::@4->memcpy#2] -- pvoz1=pvoc1 + lda #<_8 + sta.z memcpy.source + lda #>_8 + sta.z memcpy.source+1 + jsr memcpy + // main::@return + // } + // [21] return + rts + _8: .text "rules" + .byte 0 + reigns_0: .text "reigns" + .byte 0 +} + // memcpy +// Copy block of memory (forwards) +// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. +// memcpy(void* zeropage(6) destination, void* zeropage(4) source, word zeropage(8) num) +memcpy: { + .label src_end = 8 + .label dst = 6 + .label src = 4 + .label source = 4 + .label destination = 6 + .label num = 8 + // src_end = (char*)source+num + // [23] (byte*) memcpy::src_end#0 ← (byte*)(void*) memcpy::source#2 + (word) memcpy::num#2 -- pbuz1=pbuz2_plus_vwuz1 + lda.z src_end + clc + adc.z source + sta.z src_end + lda.z src_end+1 + adc.z source+1 + sta.z src_end+1 + // [24] (byte*~) memcpy::src#4 ← (byte*)(void*) memcpy::source#2 + // [25] (byte*~) memcpy::dst#4 ← (byte*)(void*) memcpy::destination#2 + // [26] phi from memcpy memcpy::@2 to memcpy::@1 [phi:memcpy/memcpy::@2->memcpy::@1] + // [26] phi (byte*) memcpy::dst#2 = (byte*~) memcpy::dst#4 [phi:memcpy/memcpy::@2->memcpy::@1#0] -- register_copy + // [26] phi (byte*) memcpy::src#2 = (byte*~) memcpy::src#4 [phi:memcpy/memcpy::@2->memcpy::@1#1] -- register_copy + // memcpy::@1 + b1: + // while(src!=src_end) + // [27] if((byte*) memcpy::src#2!=(byte*) memcpy::src_end#0) goto memcpy::@2 -- pbuz1_neq_pbuz2_then_la1 + lda.z src+1 + cmp.z src_end+1 + bne b2 + lda.z src + cmp.z src_end + bne b2 + // memcpy::@return + // } + // [28] return + rts + // memcpy::@2 + b2: + // *dst++ = *src++ + // [29] *((byte*) memcpy::dst#2) ← *((byte*) memcpy::src#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + sta (dst),y + // *dst++ = *src++; + // [30] (byte*) memcpy::dst#1 ← ++ (byte*) memcpy::dst#2 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [31] (byte*) memcpy::src#1 ← ++ (byte*) memcpy::src#2 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + jmp b1 +} + // File Data + CAMELOT: .text "camelot" + .byte 0 + diff --git a/src/test/ref/memcpy-1.sym b/src/test/ref/memcpy-1.sym new file mode 100644 index 000000000..38586f3e7 --- /dev/null +++ b/src/test/ref/memcpy-1.sym @@ -0,0 +1,61 @@ +(label) @1 +(label) @begin +(label) @end +(byte[]) CAMELOT +(const byte[]) CAMELOT#0 CAMELOT = (string) "camelot" +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(const string) main::$8 $8 = (string) "rules" +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte*) main::camelot +(byte*) main::camelot#1 camelot zp ZP_WORD:4 7.333333333333333 +(byte*) main::camelot#2 camelot zp ZP_WORD:4 11.0 +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 5.5 +(byte) main::i1 +(byte) main::i1#1 reg byte x 16.5 +(byte) main::i1#2 reg byte x 5.5 +(byte*) main::reigns +(const byte*) main::reigns#0 reigns#0 = (string) "reigns" +(byte*) main::reigns#1 reigns zp ZP_WORD:8 7.333333333333333 +(byte*) main::reigns#2 reigns zp ZP_WORD:8 11.0 +(byte*) main::sc +(byte*) main::sc#1 sc zp ZP_WORD:6 5.5 +(byte*) main::sc#2 sc zp ZP_WORD:6 16.5 +(byte*) main::sc2 +(byte*) main::sc2#1 sc2 zp ZP_WORD:2 5.5 +(byte*) main::sc2#2 sc2 zp ZP_WORD:2 16.5 +(void*()) memcpy((void*) memcpy::destination , (void*) memcpy::source , (word) memcpy::num) +(label) memcpy::@1 +(label) memcpy::@2 +(label) memcpy::@return +(void*) memcpy::destination +(void*) memcpy::destination#2 destination zp ZP_WORD:6 +(byte*) memcpy::dst +(byte*) memcpy::dst#1 dst zp ZP_WORD:6 11.0 +(byte*) memcpy::dst#2 dst zp ZP_WORD:6 11.666666666666666 +(byte*~) memcpy::dst#4 dst zp ZP_WORD:6 4.0 +(word) memcpy::num +(word) memcpy::num#2 num zp ZP_WORD:8 2.0 +(void*) memcpy::return +(void*) memcpy::source +(void*) memcpy::source#2 source zp ZP_WORD:4 +(byte*) memcpy::src +(byte*) memcpy::src#1 src zp ZP_WORD:4 22.0 +(byte*) memcpy::src#2 src zp ZP_WORD:4 11.5 +(byte*~) memcpy::src#4 src zp ZP_WORD:4 2.0 +(byte*) memcpy::src_end +(byte*) memcpy::src_end#0 src_end zp ZP_WORD:8 1.625 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::sc2#2 main::sc2#1 ] +reg byte x [ main::i1#2 main::i1#1 ] +zp ZP_WORD:4 [ memcpy::source#2 memcpy::src#2 memcpy::src#4 memcpy::src#1 main::camelot#2 main::camelot#1 ] +zp ZP_WORD:6 [ memcpy::destination#2 memcpy::dst#2 memcpy::dst#4 memcpy::dst#1 main::sc#2 main::sc#1 ] +zp ZP_WORD:8 [ memcpy::num#2 memcpy::src_end#0 main::reigns#2 main::reigns#1 ]