diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 579113205..108e34535 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -35,7 +35,7 @@ public class CompileLog { /** * Should the log be output to System.out while being built */ - private boolean sysOut = false; + private boolean sysOut = true; public CompileLog() { this.log = new StringBuilder(); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java index e7d41846a..b2e6400cd 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java @@ -190,6 +190,7 @@ public class AsmFragmentInstanceSpec { this.signature = signature; } + /** * Add bindings of a value. * @@ -197,101 +198,68 @@ public class AsmFragmentInstanceSpec { * @return The bound name of the value. If the value has already been bound the existing bound name is returned. */ public String bind(Value value) { - if(value instanceof PointerDereferenceSimple) { - PointerDereferenceSimple deref = (PointerDereferenceSimple) value; - return "_deref_" + bind(deref.getPointer()); - } else if(value instanceof PointerDereferenceIndexed) { - PointerDereferenceIndexed deref = (PointerDereferenceIndexed) value; - return bind(deref.getPointer()) + "_derefidx_" + bind(deref.getIndex()); - } + return bind(value, null); + } - if(value instanceof VariableRef) { - value = program.getSymbolInfos().getVariable((VariableRef) value); - } - if(value instanceof ConstantRef) { - value = program.getScope().getConstant((ConstantRef) value); - } + /** + * Add bindings of a value. + * + * @param value The value to bind. + * @return The bound name of the value. If the value has already been bound the existing bound name is returned. + */ + public String bind(Value value, SymbolType type) { - // Find value if it is already bound - for(String name : bindings.keySet()) { - Value bound = bindings.get(name); - if(bound.equals(value)) { - return name; - } - } - - if(value instanceof Variable) { - Variable variable = (Variable) value; - SymbolType varType = variable.getType(); - // Find the register - Registers.Register register = variable.getAllocation(); - // Examine if the register is already bound - and reuse it - String bound = findBound(varType, register); - if(bound != null) return bound; - // Bind the register - String name = getTypePrefix(varType) + getRegisterName(register); - bindings.put(name, value); - return name; - } else if(value instanceof CastValue) { + if(value instanceof CastValue) { CastValue castVal = (CastValue) value; SymbolType toType = castVal.getToType(); value = castVal.getValue(); - // Assume cast value is a symbol-ref - value = program.getSymbolInfos().getSymbol((SymbolRef) value); - if(value instanceof ConstantVar || value instanceof ConstantValue) { - String name = getTypePrefix(toType) + "c" + nextConstByteIdx++; - bindings.put(name, value); - return name; - } else if(value instanceof Variable) { - // Find the register - Variable variable = (Variable) value; - Registers.Register register = variable.getAllocation(); - // Examine if the register is already bound (with the cast to type) - and reuse it - String bound = findBound(toType, register); - if(bound != null) { - String name = getTypePrefix(toType) + getRegisterName(register); - return name; - } else { - // Bind the register - String name = getTypePrefix(toType) + getRegisterName(register); - bindings.put(name, value); - return name; - } - } else { - throw new RuntimeException("Unhandled cast to type " + value); + return bind(value, toType); + } else if(value instanceof PointerDereference) { + PointerDereference deref = (PointerDereference) value; + SymbolType ptrType = null; + if(type != null) { + ptrType = new SymbolTypePointer(type); } - } else if(value instanceof ConstantVar || value instanceof ConstantValue) { - SymbolType constType; - if(value instanceof ConstantVar) { - constType = ((ConstantVar) value).getType(); - } else { - constType = SymbolTypeInference.inferType(program.getScope(), (ConstantValue) value); + if(value instanceof PointerDereferenceSimple) { + return "_deref_" + bind(deref.getPointer(), ptrType); + } else if(value instanceof PointerDereferenceIndexed) { + PointerDereferenceIndexed derefIdx = (PointerDereferenceIndexed) value; + return bind(derefIdx.getPointer(), ptrType) + "_derefidx_" + bind(derefIdx.getIndex()); } - String name = getTypePrefix(constType) + "c" + nextConstByteIdx++; - bindings.put(name, value); + } else if(value instanceof VariableRef) { + Variable variable = program.getSymbolInfos().getVariable((VariableRef) value); + if(type == null) { + type = variable.getType(); + } + Registers.Register register = variable.getAllocation(); + String name = getTypePrefix(type) + getRegisterName(register); + bind(name, variable); + return name; + } else if(value instanceof ConstantValue) { + if(type == null) { + type = SymbolTypeInference.inferType(program.getScope(), (RValue) value); + } + String name = getTypePrefix(type) + getConstName(value); + bind(name, value); return name; } else if(value instanceof Label) { String name = "la" + nextLabelIdx++; - bindings.put(name, value); + bind(name, value); return name; } throw new RuntimeException("Binding of value type not supported " + value); } - private String findBound(SymbolType varType, Registers.Register register) { - // Find value if it is already bound - for(String name : bindings.keySet()) { - Value bound = bindings.get(name); - if(bound instanceof Variable) { - Registers.Register boundRegister = ((Variable) bound).getAllocation(); - if(boundRegister != null && boundRegister.equals(register)) { - if(SymbolTypeInference.typeMatch(((Variable) bound).getType(), varType)) { - return name; - } - } - } + /** + * Add binding for a name/value pair if it is not already bound. + * + * @param name The name + * @param value The value + */ + private void bind(String name, Value value) { + if(bindings.get(name) == null) { + bindings.put(name, value); } - return null; } /** @@ -335,10 +303,29 @@ public class AsmFragmentInstanceSpec { * @return The register part of the binding name. */ private String getRegisterName(Registers.Register register) { - if(Registers.RegisterType.ZP_BYTE.equals(register.getType())) { - return "z" + getRegisterZpNameIdx((Registers.RegisterZp) register); - } else if(Registers.RegisterType.ZP_WORD.equals(register.getType())) { - return "z" + getRegisterZpNameIdx((Registers.RegisterZp) register); + if(Registers.RegisterType.ZP_BYTE.equals(register.getType()) || Registers.RegisterType.ZP_WORD.equals(register.getType())) { + // Examine if the ZP register is already bound + Registers.RegisterZp registerZp = (Registers.RegisterZp) register; + String zpNameIdx = null; + for(String boundName : bindings.keySet()) { + Value boundValue = bindings.get(boundName); + if(boundValue instanceof Variable) { + Registers.Register boundRegister = ((Variable) boundValue).getAllocation(); + if(boundRegister != null && boundRegister.isZp()) { + Registers.RegisterZp boundRegisterZp = (Registers.RegisterZp) boundRegister; + if(registerZp.getZp() == boundRegisterZp.getZp()) { + // Found other register with same ZP address! + zpNameIdx = boundName.substring(boundName.length() - 1); + break; + } + } + } + } + // If not create a new one + if(zpNameIdx == null) { + zpNameIdx = Integer.toString(nextZpByteIdx++); + } + return "z" + zpNameIdx; } else if(Registers.RegisterType.REG_A_BYTE.equals(register.getType())) { return "aa"; } else if(Registers.RegisterType.REG_X_BYTE.equals(register.getType())) { @@ -352,31 +339,20 @@ public class AsmFragmentInstanceSpec { } } - /** - * Get the register ZP name index to use for a specific register. - * Examines all previous bindings to reuse register index if the same register is bound multiple times. - * - * @param register The register to find an index for - * @return The index. Either reused ot allocated from {@link #nextZpByteIdx} - */ - private String getRegisterZpNameIdx(Registers.RegisterZp register) { + private String getConstName(Value constant) { + // If the constant is already bound - reuse the index for(String boundName : bindings.keySet()) { Value boundValue = bindings.get(boundName); - if(boundValue instanceof Variable) { - Registers.Register boundRegister = ((Variable) boundValue).getAllocation(); - if(boundRegister != null && boundRegister.isZp()) { - Registers.RegisterZp boundRegisterZp = (Registers.RegisterZp) boundRegister; - if(register.getZp() == boundRegisterZp.getZp()) { - // Found other register with same ZP address! - return boundName.substring(boundName.length() - 1); - } + if(boundValue instanceof ConstantValue || boundValue instanceof ConstantVar) { + if(boundValue.equals(constant)) { + return "c" + boundName.substring(boundName.length() - 1); } } } - return Integer.toString(nextZpByteIdx++); + // Otherwise use a new index + return "c" + nextConstByteIdx++; } - public Program getProgram() { return program; } diff --git a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 index 8013cab04..a9a3f3bc1 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 +++ b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 @@ -6,7 +6,7 @@ file ; asmFile - : bodyAsm EOF + : asmLines EOF ; importSeq @@ -58,7 +58,7 @@ stmt | 'do' stmt 'while' '(' expr ')' ';' #stmtDoWhile | 'for' '(' forDeclaration? forIteration ')' stmt #stmtFor | 'return' expr? ';' #stmtReturn - | 'asm' '{' bodyAsm '}' #stmtAsm + | 'asm' '{' asmLines '}' #stmtAsm ; forDeclaration @@ -80,8 +80,8 @@ typeDecl expr : '(' expr ')' #exprPar | NAME '(' parameterList? ')' #exprCall - | '(' typeDecl ')' expr #exprCast | expr '[' expr ']' #exprArray + | '(' typeDecl ')' expr #exprCast | ('--' | '++' ) expr #exprPreMod | expr ('--' | '++' ) #exprPostMod | '*' expr #exprPtr @@ -109,7 +109,7 @@ parameterList : expr (',' expr)* ; -bodyAsm +asmLines : asmLine* ; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCParser.java b/src/main/java/dk/camelot64/kickc/parser/KickCParser.java index af18a65b1..2542a40b5 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCParser.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCParser.java @@ -2063,7 +2063,7 @@ public class KickCParser extends Parser { setState(234); match(T__2); setState(235); - expr(23); + expr(22); } break; case 4: @@ -2388,7 +2388,7 @@ public class KickCParser extends Parser { _localctx = new ExprArrayContext(new ExprContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expr); setState(293); - if (!(precpred(_ctx, 22))) throw new FailedPredicateException(this, "precpred(_ctx, 22)"); + if (!(precpred(_ctx, 23))) throw new FailedPredicateException(this, "precpred(_ctx, 23)"); setState(294); match(T__21); setState(295); @@ -3384,7 +3384,7 @@ public class KickCParser extends Parser { case 11: return precpred(_ctx, 7); case 12: - return precpred(_ctx, 22); + return precpred(_ctx, 23); case 13: return precpred(_ctx, 20); } @@ -3489,7 +3489,7 @@ public class KickCParser extends Parser { "\7\5\2\2\u00e3\u0108\3\2\2\2\u00e4\u00e5\7B\2\2\u00e5\u00e7\7\4\2\2\u00e6"+ "\u00e8\5$\23\2\u00e7\u00e6\3\2\2\2\u00e7\u00e8\3\2\2\2\u00e8\u00e9\3\2"+ "\2\2\u00e9\u0108\7\5\2\2\u00ea\u00eb\7\4\2\2\u00eb\u00ec\5 \21\2\u00ec"+ - "\u00ed\7\5\2\2\u00ed\u00ee\5\"\22\31\u00ee\u0108\3\2\2\2\u00ef\u00f0\t"+ + "\u00ed\7\5\2\2\u00ed\u00ee\5\"\22\30\u00ee\u0108\3\2\2\2\u00ef\u00f0\t"+ "\2\2\2\u00f0\u0108\5\"\22\27\u00f1\u00f2\7\27\2\2\u00f2\u0108\5\"\22\25"+ "\u00f3\u00f4\t\3\2\2\u00f4\u0108\5\"\22\24\u00f5\u00f6\t\4\2\2\u00f6\u0108"+ "\5\"\22\20\u00f7\u00f8\7\6\2\2\u00f8\u00fd\5\"\22\2\u00f9\u00fa\7\b\2"+ @@ -3510,7 +3510,7 @@ public class KickCParser extends Parser { "\u012f\5\"\22\r\u011e\u011f\f\13\2\2\u011f\u0120\7\60\2\2\u0120\u012f"+ "\5\"\22\f\u0121\u0122\f\n\2\2\u0122\u0123\7\61\2\2\u0123\u012f\5\"\22"+ "\13\u0124\u0125\f\t\2\2\u0125\u0126\7\t\2\2\u0126\u012f\5\"\22\t\u0127"+ - "\u0128\f\30\2\2\u0128\u0129\7\30\2\2\u0129\u012a\5\"\22\2\u012a\u012b"+ + "\u0128\f\31\2\2\u0128\u0129\7\30\2\2\u0129\u012a\5\"\22\2\u012a\u012b"+ "\7\31\2\2\u012b\u012f\3\2\2\2\u012c\u012d\f\26\2\2\u012d\u012f\t\2\2\2"+ "\u012e\u0109\3\2\2\2\u012e\u010c\3\2\2\2\u012e\u010f\3\2\2\2\u012e\u0112"+ "\3\2\2\2\u012e\u0115\3\2\2\2\u012e\u0118\3\2\2\2\u012e\u011b\3\2\2\2\u012e"+ diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 57a5b4236..0ee64aae3 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -145,6 +145,9 @@ public class Pass4CodeGeneration { if(SymbolType.isByte(constantArrayList.getElementType())) { asm.addDataNumeric(asmName.replace("#", "_").replace("$", "_"), AsmDataNumeric.Type.BYTE, asmElements); added.add(asmName); + } else if(SymbolType.isSByte(constantArrayList.getElementType())) { + asm.addDataNumeric(asmName.replace("#", "_").replace("$", "_"), AsmDataNumeric.Type.BYTE, asmElements); + added.add(asmName); } else { throw new RuntimeException("Unhandled constant array element type " + constantArrayList.toString(program)); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index bf66d5fd1..f106d579a 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,6 +42,11 @@ public class TestPrograms { AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } + @Test + public void testCastDeref() throws IOException, URISyntaxException { + compileAndCompare("cast-deref"); + } + @Test public void testRasterBars() throws IOException, URISyntaxException { compileAndCompare("raster-bars"); diff --git a/src/test/java/dk/camelot64/kickc/test/kc/cast-deref.kc b/src/test/java/dk/camelot64/kickc/test/kc/cast-deref.kc new file mode 100644 index 000000000..39f72866d --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/kc/cast-deref.kc @@ -0,0 +1,9 @@ +// Example of NOP-casting a dereferenced signed byte to a byte + +void main() { + signed byte[] sbs = { -1, -2, -3, -4}; + byte* SCREEN = $0400; + for(byte i : 0..3) { + SCREEN[i] = (byte) sbs[i]; + } +} \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.asm b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.asm new file mode 100644 index 000000000..3a1f56f38 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.asm @@ -0,0 +1,16 @@ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + jsr main +main: { + .const SCREEN = $400 + ldx #0 + b1: + lda sbs,x + sta SCREEN,x + inx + cpx #4 + bne b1 + rts + sbs: .byte -1, -2, -3, -4 +} diff --git a/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.cfg b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.cfg new file mode 100644 index 000000000..a67e93cd0 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.cfg @@ -0,0 +1,21 @@ +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@1 +@1: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main param-assignment [ ] ( ) + to:@end +@end: scope:[] from @1 + [3] phi() [ ] ( ) +main: scope:[main] from @1 + [4] phi() [ ] ( main:2 [ ] ) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word) 0 main::@1/(byte) main::i#1 ) [ main::i#2 ] ( main:2 [ main::i#2 ] ) + [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) + [7] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:2 [ main::i#1 ] ) + [8] if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 [ main::i#1 ] ( main:2 [ main::i#1 ] ) + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return [ ] ( main:2 [ ] ) + to:@return diff --git a/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.log b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.log new file mode 100644 index 000000000..8f69ded05 --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.log @@ -0,0 +1,445 @@ +PARSING src/test/java/dk/camelot64/kickc/test/kc/cast-deref.kc +// Example of NOP-casting a dereferenced signed byte to a byte + +void main() { + signed byte[] sbs = { -1, -2, -3, -4}; + byte* SCREEN = $0400; + for(byte i : 0..3) { + SCREEN[i] = (byte) sbs[i]; + } +} + +STATEMENTS +proc (void()) main() + (signed byte/signed word~) main::$0 ← - (byte/signed byte/word/signed word) 1 + (signed byte/signed word~) main::$1 ← - (byte/signed byte/word/signed word) 2 + (signed byte/signed word~) main::$2 ← - (byte/signed byte/word/signed word) 3 + (signed byte/signed word~) main::$3 ← - (byte/signed byte/word/signed word) 4 + (signed byte[]) main::sbs ← { (signed byte/signed word~) main::$0, (signed byte/signed word~) main::$1, (signed byte/signed word~) main::$2, (signed byte/signed word~) main::$3 } + (byte*) main::SCREEN ← (word/signed word) 1024 + (byte) main::i ← (byte/signed byte/word/signed word) 0 +main::@1: + (byte~) main::$4 ← ((byte)) *((signed byte[]) main::sbs + (byte) main::i) + *((byte*) main::SCREEN + (byte) main::i) ← (byte~) main::$4 + (byte) main::i ← ++ (byte) main::i + (boolean~) main::$5 ← (byte) main::i != (byte/signed byte/word/signed word) 4 + if((boolean~) main::$5) goto main::@1 +main::@return: + return +endproc // main() + call main + +SYMBOLS +(void()) main() +(signed byte/signed word~) main::$0 +(signed byte/signed word~) main::$1 +(signed byte/signed word~) main::$2 +(signed byte/signed word~) main::$3 +(byte~) main::$4 +(boolean~) main::$5 +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(byte) main::i +(signed byte[]) main::sbs + +Promoting word/signed word to byte* in main::SCREEN ← ((byte*)) 1024 +INITIAL CONTROL FLOW GRAPH +@begin: scope:[] from + to:@1 +main: scope:[main] from + (signed byte/signed word~) main::$0 ← - (byte/signed byte/word/signed word) 1 + (signed byte/signed word~) main::$1 ← - (byte/signed byte/word/signed word) 2 + (signed byte/signed word~) main::$2 ← - (byte/signed byte/word/signed word) 3 + (signed byte/signed word~) main::$3 ← - (byte/signed byte/word/signed word) 4 + (signed byte[]) main::sbs ← { (signed byte/signed word~) main::$0, (signed byte/signed word~) main::$1, (signed byte/signed word~) main::$2, (signed byte/signed word~) main::$3 } + (byte*) main::SCREEN ← ((byte*)) (word/signed word) 1024 + (byte) main::i ← (byte/signed byte/word/signed word) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte~) main::$4 ← ((byte)) *((signed byte[]) main::sbs + (byte) main::i) + *((byte*) main::SCREEN + (byte) main::i) ← (byte~) main::$4 + (byte) main::i ← ++ (byte) main::i + (boolean~) main::$5 ← (byte) main::i != (byte/signed byte/word/signed word) 4 + if((boolean~) main::$5) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return +@1: scope:[] from @begin + call main + to:@end +@end: scope:[] from @1 + +Removing empty block main::@2 +PROCEDURE MODIFY VARIABLE ANALYSIS + +Completing Phi functions... + +CONTROL FLOW GRAPH SSA WITH ASSIGNMENT CALL & RETURN +@begin: scope:[] from + to:@1 +main: scope:[main] from @1 + (signed byte/signed word~) main::$0 ← - (byte/signed byte/word/signed word) 1 + (signed byte/signed word~) main::$1 ← - (byte/signed byte/word/signed word) 2 + (signed byte/signed word~) main::$2 ← - (byte/signed byte/word/signed word) 3 + (signed byte/signed word~) main::$3 ← - (byte/signed byte/word/signed word) 4 + (signed byte[]) main::sbs#0 ← { (signed byte/signed word~) main::$0, (signed byte/signed word~) main::$1, (signed byte/signed word~) main::$2, (signed byte/signed word~) main::$3 } + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word) 1024 + (byte) main::i#0 ← (byte/signed byte/word/signed word) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte*) main::SCREEN#1 ← phi( main/(byte*) main::SCREEN#0 main::@1/(byte*) main::SCREEN#1 ) + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@1/(byte) main::i#1 ) + (byte~) main::$4 ← ((byte)) *((signed byte[]) main::sbs#0 + (byte) main::i#2) + *((byte*) main::SCREEN#1 + (byte) main::i#2) ← (byte~) main::$4 + (byte) main::i#1 ← ++ (byte) main::i#2 + (boolean~) main::$5 ← (byte) main::i#1 != (byte/signed byte/word/signed word) 4 + if((boolean~) main::$5) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + call main param-assignment + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) main() +(signed byte/signed word~) main::$0 +(signed byte/signed word~) main::$1 +(signed byte/signed word~) main::$2 +(signed byte/signed word~) main::$3 +(byte~) main::$4 +(boolean~) main::$5 +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(byte*) main::SCREEN#1 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(signed byte[]) main::sbs +(signed byte[]) main::sbs#0 + +OPTIMIZING CONTROL FLOW GRAPH +Culled Empty Block (label) @2 +Succesful SSA optimization Pass2CullEmptyBlocks +Self Phi Eliminated (byte*) main::SCREEN#1 +Succesful SSA optimization Pass2SelfPhiElimination +Redundant Phi (byte*) main::SCREEN#1 (byte*) main::SCREEN#0 +Succesful SSA optimization Pass2RedundantPhiElimination +Simple Condition (boolean~) main::$5 if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 +Succesful SSA optimization Pass2ConditionalJumpSimplification +Constant (const signed byte/signed word) main::$0 = -1 +Constant (const signed byte/signed word) main::$1 = -2 +Constant (const signed byte/signed word) main::$2 = -3 +Constant (const signed byte/signed word) main::$3 = -4 +Constant (const byte*) main::SCREEN#0 = ((byte*))1024 +Constant (const byte) main::i#0 = 0 +Succesful SSA optimization Pass2ConstantIdentification +Constant (const signed byte[]) main::sbs#0 = { main::$0, main::$1, main::$2, main::$3 } +Succesful SSA optimization Pass2ConstantIdentification +Eliminating Noop Cast (byte~) main::$4 ← ((byte)) *((const signed byte[]) main::sbs#0 + (byte) main::i#2) +Succesful SSA optimization Pass2NopCastElimination +OPTIMIZING CONTROL FLOW GRAPH +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte/signed byte/word/signed word) 0 +Constant inlined main::$3 = -(byte/signed byte/word/signed word) 4 +Constant inlined main::$1 = -(byte/signed byte/word/signed word) 2 +Constant inlined main::$2 = -(byte/signed byte/word/signed word) 3 +Constant inlined main::$0 = -(byte/signed byte/word/signed word) 1 +Succesful SSA optimization Pass2ConstantInlining +Block Sequence Planned @begin @1 @end main main::@1 main::@return +Added new block during phi lifting main::@3(between main::@1 and main::@1) +Block Sequence Planned @begin @1 @end main main::@1 main::@return main::@3 +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 +CALL GRAPH +Calls in [] to main:2 + +Propagating live ranges... +Propagating live ranges... +Created 1 initial phi equivalence classes +Coalesced [10] main::i#3 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@3 +Block Sequence Planned @begin @1 @end main main::@1 main::@return +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 +Propagating live ranges... +Propagating live ranges... + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() [ ] ( ) + to:@1 +@1: scope:[] from @begin + [1] phi() [ ] ( ) + [2] call main param-assignment [ ] ( ) + to:@end +@end: scope:[] from @1 + [3] phi() [ ] ( ) +main: scope:[main] from @1 + [4] phi() [ ] ( main:2 [ ] ) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word) 0 main::@1/(byte) main::i#1 ) [ main::i#2 ] ( main:2 [ main::i#2 ] ) + [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) + [7] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:2 [ main::i#1 ] ) + [8] if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 [ main::i#1 ] ( main:2 [ main::i#1 ] ) + to:main::@return +main::@return: scope:[main] from main::@1 + [9] return [ ] ( main:2 [ ] ) + to:@return + +DOMINATORS +@begin dominated by @begin +@1 dominated by @1 @begin +@end dominated by @1 @begin @end +main dominated by @1 @begin main +main::@1 dominated by @1 @begin main::@1 main +main::@return dominated by main::@return @1 @begin main::@1 main + +NATURAL LOOPS +Found back edge: Loop head: main::@1 tails: main::@1 blocks: null +Populated: Loop head: main::@1 tails: main::@1 blocks: main::@1 +Loop head: main::@1 tails: main::@1 blocks: main::@1 + +NATURAL LOOPS WITH DEPTH +Found 0 loops in scope [] +Found 1 loops in scope [main] + Loop head: main::@1 tails: main::@1 blocks: main::@1 +Loop head: main::@1 tails: main::@1 blocks: main::@1 depth: 1 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte*) main::SCREEN +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 16.5 +(signed byte[]) main::sbs + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] + +INITIAL ASM +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .const SCREEN = $400 + .label i = 2 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG11 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG13 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG14 main::@1 + b1: + //SEG15 [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy i + lda sbs,y + sta SCREEN,y + //SEG16 [7] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuz1=_inc_vbuz1 + inc i + //SEG17 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuz1_neq_vbuc1_then_la1 + lda i + cmp #4 + bne b1_from_b1 + jmp breturn + //SEG18 main::@return + breturn: + //SEG19 [9] return [ ] ( main:2 [ ] ) + rts + sbs: .byte -1, -2, -3, -4 +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 33: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 288 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 288 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +bbegin: +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG4 @1 +b1: +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .const SCREEN = $400 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG11 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG13 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG14 main::@1 + b1: + //SEG15 [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda sbs,x + sta SCREEN,x + //SEG16 [7] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuxx=_inc_vbuxx + inx + //SEG17 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuxx_neq_vbuc1_then_la1 + cpx #4 + bne b1_from_b1 + jmp breturn + //SEG18 main::@return + breturn: + //SEG19 [9] return [ ] ( main:2 [ ] ) + rts + sbs: .byte -1, -2, -3, -4 +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b1 with b1 +Removing instruction bbegin: +Removing instruction b1_from_bbegin: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b1: +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word) 1024 +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 16.5 +(signed byte[]) main::sbs +(const signed byte[]) main::sbs#0 sbs = { -(byte/signed byte/word/signed word) 1, -(byte/signed byte/word/signed word) 2, -(byte/signed byte/word/signed word) 3, -(byte/signed byte/word/signed word) 4 } + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 192 + +//SEG0 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG1 Global Constants & labels +//SEG2 @begin +//SEG3 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG4 @1 +//SEG5 [2] call main param-assignment [ ] ( ) +//SEG6 [4] phi from @1 to main [phi:@1->main] + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .const SCREEN = $400 + //SEG10 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG11 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG12 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + //SEG13 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG14 main::@1 + b1: + //SEG15 [6] *((const byte*) main::SCREEN#0 + (byte) main::i#2) ← (byte)*((const signed byte[]) main::sbs#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuxx + lda sbs,x + sta SCREEN,x + //SEG16 [7] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuxx=_inc_vbuxx + inx + //SEG17 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word) 4) goto main::@1 [ main::i#1 ] ( main:2 [ main::i#1 ] ) -- vbuxx_neq_vbuc1_then_la1 + cpx #4 + bne b1 + //SEG18 main::@return + //SEG19 [9] return [ ] ( main:2 [ ] ) + rts + sbs: .byte -1, -2, -3, -4 +} + diff --git a/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.sym b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.sym new file mode 100644 index 000000000..08be2f9eb --- /dev/null +++ b/src/test/java/dk/camelot64/kickc/test/ref/cast-deref.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word) 1024 +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 16.5 +(signed byte[]) main::sbs +(const signed byte[]) main::sbs#0 sbs = { -(byte/signed byte/word/signed word) 1, -(byte/signed byte/word/signed word) 2, -(byte/signed byte/word/signed word) 3, -(byte/signed byte/word/signed word) 4 } + +reg byte x [ main::i#2 main::i#1 ]