From 4807bbded790f86a4c14ce36596a9ee00162ee04 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 4 Aug 2019 00:13:33 +0200 Subject: [PATCH] Code generation now handles encoding in literal chars. Done 2/3 of #245 --- .../AsmFragmentInstanceSpecFactory.java | 4 + .../kickc/model/iterator/ProgramValue.java | 10 +- .../model/iterator/ProgramValueIterator.java | 6 +- .../kickc/model/values/ConstantChar.java | 4 + .../kickc/passes/Pass4CodeGeneration.java | 96 +++++++++++++++++-- .../dk/camelot64/kickc/test/TestPrograms.java | 4 +- src/test/ref/kc-ka-string-encoding.asm | 2 +- src/test/ref/kc-ka-string-encoding.log | 6 +- 8 files changed, 111 insertions(+), 21 deletions(-) diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index 67307d393..86508f363 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -43,6 +43,10 @@ public class AsmFragmentInstanceSpecFactory { private int nextConstIdx = 1; private int nextLabelIdx = 1; + public Map getBindings() { + return bindings; + } + public AsmFragmentInstanceSpecFactory( StatementConditionalJump conditionalJump, ControlFlowBlock block, diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java index 5f48cf515..e8086431e 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java @@ -250,20 +250,20 @@ public interface ProgramValue { /** A generic Value. */ class GenericValue implements ProgramValue { - private RValue rValue; + private Value value; - public GenericValue(RValue rValue) { - this.rValue = rValue; + public GenericValue(Value value) { + this.value = value; } @Override public Value get() { - return rValue; + return value; } @Override public void set(Value value) { - this.rValue = (RValue) value; + this.value = value; } } diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java index b72c4616a..a6dd81237 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java @@ -4,9 +4,7 @@ import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.ControlFlowGraph; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.*; -import dk.camelot64.kickc.model.symbols.ConstantVar; -import dk.camelot64.kickc.model.symbols.ProgramScope; -import dk.camelot64.kickc.model.symbols.SymbolVariable; +import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolTypeArray; import dk.camelot64.kickc.model.values.*; @@ -232,6 +230,8 @@ public class ProgramValueIterator { subValues.add(new ProgramValue.ProgramValueLValueIntermediateVariable((LvalueIntermediate) value)); } else if(value == null || value instanceof VariableRef || + value instanceof VariableVersion || + value instanceof VariableIntermediate || value instanceof ProcedureRef || value instanceof ConstantLiteral || value instanceof ConstantRef || diff --git a/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java b/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java index 2cd742681..e722a654e 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java @@ -34,6 +34,10 @@ public class ConstantChar implements ConstantLiteral { return value; } + public ConstantString.Encoding getEncoding() { + return encoding; + } + @Override public String toString() { return toString(null); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 5aec2ed45..2fafdf8b7 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -4,6 +4,9 @@ import dk.camelot64.kickc.asm.*; import dk.camelot64.kickc.fragment.*; import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.InternalError; +import dk.camelot64.kickc.model.iterator.ProgramValue; +import dk.camelot64.kickc.model.iterator.ProgramValueHandler; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.*; @@ -270,6 +273,8 @@ public class Pass4CodeGeneration { added.add(asmName); // Add any comments generateComments(asm, constantVar.getComments()); + // Ensure encoding is good + ensureEncoding(asm, constantVar.getValue()); // Find the constant value calculation String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getValue(), 99, scopeRef); if(constantVar.getType() instanceof SymbolTypePointer) { @@ -419,6 +424,7 @@ public class Pass4CodeGeneration { asm.addLabel(asmName).setDontOptimize(true); for(ConstantValue element : constantArrayList.getElements()) { AsmProgram.AsmDataNumericChunk asmDataChunk = new AsmProgram.AsmDataNumericChunk(); + ensureEncoding(asm, element); addChunkData(asmDataChunk, element, scopeRef); asm.addDataNumeric(null, asmDataChunk); } @@ -429,6 +435,7 @@ public class Pass4CodeGeneration { // Constant array of a "simple" type - add to a single chunk AsmProgram.AsmDataNumericChunk asmDataChunk = new AsmProgram.AsmDataNumericChunk(); for(ConstantValue element : constantArrayList.getElements()) { + ensureEncoding(asm, element); addChunkData(asmDataChunk, element, scopeRef); } asm.addDataNumeric(asmName, asmDataChunk); @@ -436,6 +443,8 @@ public class Pass4CodeGeneration { } else if(constantVar.getValue() instanceof ConstantArrayFilled) { ConstantArrayFilled constantArrayFilled = (ConstantArrayFilled) constantVar.getValue(); ConstantValue arraySize = constantArrayFilled.getSize(); + // ensure encoding is good + ensureEncoding(asm, arraySize); ConstantLiteral arraySizeConst = arraySize.calculateLiteral(getScope()); if(!(arraySizeConst instanceof ConstantInteger)) { throw new Pass2SsaAssertion.AssertionFailed("Error! Array size is not constant integer " + constantVar.toString(program)); @@ -497,11 +506,8 @@ public class Pass4CodeGeneration { try { ConstantLiteral literal = constantVar.getValue().calculateLiteral(getScope()); if(literal instanceof ConstantString) { - ConstantString.Encoding stringEncoding = ((ConstantString) literal).getEncoding(); - if(!currentEncoding.equals(stringEncoding)) { - asm.addLine(new AsmSetEncoding(stringEncoding)); - currentEncoding = stringEncoding; - } + // Ensure encoding is good + ensureEncoding(asm, constantVar.getValue()); String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getValue(), 99, scopeRef); asm.addDataString(asmName.replace("#", "_").replace("$", "_"), asmConstant); added.add(asmName); @@ -514,11 +520,12 @@ public class Pass4CodeGeneration { } } + /** * Fill the data of a constant value into a data chunk * * @param dataChunk The data chunk - * @param structValue The constant value + * @param value The constant value */ private void addChunkData(AsmProgram.AsmDataNumericChunk dataChunk, ConstantValue value, ScopeRef scopeRef) { SymbolType elementType = value.getType(program.getScope()); @@ -538,7 +545,7 @@ public class Pass4CodeGeneration { } else if(elementType instanceof SymbolTypePointer) { dataChunk.addData(AsmDataNumeric.Type.WORD, AsmFormat.getAsmConstant(program, value, 99, scopeRef)); } else { - throw new InternalError("Unhandled array element type "+elementType.toString()+ " value "+value.toString(program)); + throw new InternalError("Unhandled array element type " + elementType.toString() + " value " + value.toString(program)); } } @@ -629,6 +636,7 @@ public class Pass4CodeGeneration { //asm.addComment(lValue.toString(program) + " = " + assignment.getrValue2().toString(program) + " // register copy " + getRegister(lValue)); } else { AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, program); + ensureEncoding(asm, asmFragmentInstanceSpecFactory); generateAsm(asm, asmFragmentInstanceSpecFactory); } } @@ -696,6 +704,7 @@ public class Pass4CodeGeneration { if(procedure instanceof PointerDereferenceSimple) { RValue pointer = ((PointerDereferenceSimple) procedure).getPointer(); if(pointer instanceof ConstantValue) { + ensureEncoding(asm, pointer); asm.addInstruction("jsr", AsmAddressingMode.ABS, AsmFormat.getAsmConstant(program, (ConstantValue) pointer, 99, block.getScope()), false); supported = true; } else if(pointer instanceof VariableRef) { @@ -916,6 +925,79 @@ public class Pass4CodeGeneration { } } + + /** + * Ensure that the current encoding in the ASM matches any encoding in the data to be emitted + * + * @param asm The ASM program (where any .encoding directive will be emitted) + * @param asmFragmentInstance The ASM fragment to be emitted + */ + private void ensureEncoding(AsmProgram asm, AsmFragmentInstanceSpecFactory asmFragmentInstance) { + ensureEncoding(asm, getEncoding(asmFragmentInstance)); + } + + private void ensureEncoding(AsmProgram asm, Value value) { + ensureEncoding(asm, getEncoding(value)); + } + + /** + * Ensure that the current encoding in the ASM matches any encoding in the data to be emitted + * + * @param asm The ASM program (where any .encoding directive will be emitted) + * @param encodings The encodings to ensure + */ + private void ensureEncoding(AsmProgram asm, Collection encodings) { + if(encodings == null || encodings.size()==0) return; + if(encodings.size()>1) { + throw new CompileError("Different character encodings in one ASM statement not supported!"); + } + // Size is 1 - grab it! + ConstantString.Encoding encoding = encodings.iterator().next(); + if(!currentEncoding.equals(encoding)) { + asm.addLine(new AsmSetEncoding(encoding)); + currentEncoding = encoding; + } + } + + /** + * Examine a constantvalue to see if any string encoding information is present + * + * @param value The constant to examine + * @return Any encoding found inside the constant + */ + private Set getEncoding(Value value) { + LinkedHashSet encodings = new LinkedHashSet<>(); + ProgramValue programValue = new ProgramValue.GenericValue(value); + ProgramValueHandler handler = (ProgramValue pVal, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) -> { + Value val = pVal.get(); + if(val instanceof ConstantChar) { + encodings.add(((ConstantChar) val).getEncoding()); + } else if(val instanceof ConstantString) { + encodings.add(((ConstantString) val).getEncoding()); + } + }; + ProgramValueIterator.execute(programValue, handler, null, null, null); + return encodings; + } + + + /** + * Examine an ASM fragment to see if any string encoding information is present + * + * @param asmFragmentInstance The asm fragment instance to examine + * @return Any encoding found inside the constant + */ + private Set getEncoding(AsmFragmentInstanceSpecFactory asmFragmentInstance) { + LinkedHashSet encodings = new LinkedHashSet<>(); + Map bindings = asmFragmentInstance.getBindings(); + for(Value boundValue : bindings.values()) { + encodings.addAll(getEncoding(boundValue)); + } + return encodings; + } + + + /** * Get phi transitions for a specific to-block. * diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 85d6df879..b0db67654 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -38,12 +38,12 @@ public class TestPrograms { @Test public void testKcKaStringEncoding() throws IOException, URISyntaxException { - compileAndCompare("kc-ka-string-encoding", log()); + compileAndCompare("kc-ka-string-encoding"); } @Test public void testGlobalPcMultiple() throws IOException, URISyntaxException { - compileAndCompare("global-pc-multiple", log()); + compileAndCompare("global-pc-multiple"); } diff --git a/src/test/ref/kc-ka-string-encoding.asm b/src/test/ref/kc-ka-string-encoding.asm index 500fe0c8a..039ab8bf8 100644 --- a/src/test/ref/kc-ka-string-encoding.asm +++ b/src/test/ref/kc-ka-string-encoding.asm @@ -2,6 +2,7 @@ :BasicUpstart(main) .pc = $80d "Program" main: { + .encoding "petscii_mixed" lda #'e' sta strTemp+2 lda #0 @@ -16,5 +17,4 @@ main: { done: rts } -.encoding "petscii_mixed" strTemp: .text "v=X@" diff --git a/src/test/ref/kc-ka-string-encoding.log b/src/test/ref/kc-ka-string-encoding.log index ac57cf45a..1c78682d3 100644 --- a/src/test/ref/kc-ka-string-encoding.log +++ b/src/test/ref/kc-ka-string-encoding.log @@ -140,6 +140,7 @@ bend: // main main: { // [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2 + .encoding "petscii_mixed" lda #'e' sta strTemp+2 // [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2 @@ -161,7 +162,6 @@ main: { rts } // File Data -.encoding "petscii_mixed" strTemp: .text "v=X@" REGISTER UPLIFT POTENTIAL REGISTERS @@ -200,6 +200,7 @@ bend: // main main: { // [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2 + .encoding "petscii_mixed" lda #'e' sta strTemp+2 // [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2 @@ -221,7 +222,6 @@ main: { rts } // File Data -.encoding "petscii_mixed" strTemp: .text "v=X@" ASSEMBLER OPTIMIZATIONS @@ -274,6 +274,7 @@ Score: 38 main: { // strTemp[2] = 'e' // [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2 + .encoding "petscii_mixed" lda #'e' sta strTemp+2 // strTemp[3] = 0 @@ -296,6 +297,5 @@ main: { rts } // File Data -.encoding "petscii_mixed" strTemp: .text "v=X@"