From fe60b31f73113438f9aa780c44f82ff8d5719660 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 12 May 2019 21:18:55 +0200 Subject: [PATCH] New literal word constructor handling - 277/354 --- .../java/dk/camelot64/kickc/Compiler.java | 1 + .../iterator/ProgramExpressionIterator.java | 6 +- .../iterator/ProgramExpressionUnary.java | 37 ++++++++- .../kickc/model/types/SymbolTypeArray.java | 6 +- .../model/types/SymbolTypeInference.java | 81 ++----------------- .../kickc/model/values/ValueList.java | 4 + .../Pass2ConstantCastSimplification.java | 31 ------- .../passes/Pass2FixInlineConstructorsNew.java | 69 ++++++++++++++++ .../PassNAddTypeConversionAssignment.java | 19 +++++ .../dk/camelot64/kickc/test/TestPrograms.java | 15 ++++ src/test/kc/inline-word-0.kc | 7 ++ src/test/kc/inline-word-1.kc | 7 ++ src/test/kc/inline-word-2.kc | 7 ++ 13 files changed, 174 insertions(+), 116 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructorsNew.java create mode 100644 src/test/kc/inline-word-0.kc create mode 100644 src/test/kc/inline-word-1.kc create mode 100644 src/test/kc/inline-word-2.kc diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 2e73baad4..4d524e483 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -254,6 +254,7 @@ public class Compiler { optimizations.add(new Pass2ConstantIfs(program)); optimizations.add(new Pass2ConstantStringConsolidation(program)); optimizations.add(new Pass2FixInlineConstructors(program)); + optimizations.add(new Pass2FixInlineConstructorsNew(program)); optimizations.add(new Pass2TypeInference(program)); optimizations.add(new PassNEliminateUnusedVars(program, true)); optimizations.add(new Pass2EliminateRedundantCasts(program)); diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionIterator.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionIterator.java index c109a3001..6a6d9c3dc 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionIterator.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionIterator.java @@ -5,9 +5,7 @@ import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementConditionalJump; -import dk.camelot64.kickc.model.values.ConstantBinary; -import dk.camelot64.kickc.model.values.ConstantUnary; -import dk.camelot64.kickc.model.values.PointerDereferenceIndexed; +import dk.camelot64.kickc.model.values.*; import java.util.ListIterator; @@ -33,6 +31,8 @@ public class ProgramExpressionIterator { handler.execute(new ProgramExpressionUnary.ProgramExpressionUnaryConstant(programValue), null, null, null); } else if(programValue.get() instanceof PointerDereferenceIndexed) { handler.execute(new ProgramExpressionBinary.ProgramExpressionBinaryPointerDereferenceIndexed(programValue), null, null, null); + } else if(programValue.get() instanceof ConstantCastValue) { + handler.execute(new ProgramExpressionUnary.ProgramExpressionUnaryCast(programValue), null, null, null); } }; ProgramValueIterator.execute(program.getScope(), programValueHandler); diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionUnary.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionUnary.java index a2795032d..fdcdeb4a0 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionUnary.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramExpressionUnary.java @@ -1,7 +1,9 @@ package dk.camelot64.kickc.model.iterator; import dk.camelot64.kickc.model.operators.OperatorUnary; +import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.values.ConstantCastValue; import dk.camelot64.kickc.model.values.ConstantUnary; import dk.camelot64.kickc.model.values.RValue; import dk.camelot64.kickc.model.values.Value; @@ -52,7 +54,7 @@ public interface ProgramExpressionUnary extends ProgramExpression { /** Unary expression as part of a constant expression. */ class ProgramExpressionUnaryConstant implements ProgramExpressionUnary { - + /** A ProgramValue containing a {@link ConstantUnary}. */ private ProgramValue programValue; @@ -81,4 +83,37 @@ public interface ProgramExpressionUnary extends ProgramExpression { } + /** Unary cast expression {@link ConstantCastValue} as part of a constant expression. */ + class ProgramExpressionUnaryCast implements ProgramExpressionUnary { + + /** A ProgramValue containing a {@link ConstantCastValue}. */ + private ProgramValue programValue; + + ProgramExpressionUnaryCast(ProgramValue programValue) { + this.programValue = programValue; + } + + public ConstantCastValue getConstantUnary() { + return (ConstantCastValue) programValue.get(); + } + + @Override + public OperatorUnary getOperator() { + return Operators.getCastUnary(getConstantUnary().getToType()); + } + + @Override + public RValue getOperand() { + return getConstantUnary().getValue(); + } + + @Override + public void set(Value value) { + programValue.set(value); + } + + } + + + } diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeArray.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeArray.java index 738b40754..7aaa8fce2 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeArray.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeArray.java @@ -31,11 +31,7 @@ public class SymbolTypeArray extends SymbolTypePointer { @Override public String getTypeName() { SymbolType elementType = getElementType(); - if(elementType instanceof SymbolTypeMulti) { - return "(" + elementType.getTypeName() + ")" + "[" + (size == null ? "" : size.toString()) + "]"; - } else { - return elementType.getTypeName() + "[" + (size == null ? "" : size.toString()) + "]"; - } + return elementType.getTypeName() + "[" + (size == null ? "" : size.toString()) + "]"; } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java index 100244aca..d512f96e7 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java @@ -9,10 +9,6 @@ import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.values.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - /** * Type inference of expressions (rValues & unary/binary operators) */ @@ -126,7 +122,7 @@ public class SymbolTypeInference { } else if(rValue instanceof ProcedureRef) { Procedure procedure = symbols.getProcedure((ProcedureRef) rValue); return procedure.getType(); - } else if(rValue instanceof AssignmentRValue) { + } else if(rValue instanceof AssignmentRValue) { return inferTypeRValue(symbols, ((AssignmentRValue) rValue).getAssignment()); } if(type == null) { @@ -148,21 +144,8 @@ public class SymbolTypeInference { } } if(elmType != null) { - if((list.getList().size() == 2 && SymbolType.BYTE.equals(elmType) || SymbolType.SBYTE.equals(elmType))) { - // Potentially a word constructor - return a composite type - ArrayList types = new ArrayList<>(); - types.add(new SymbolTypeArray(elmType)); - types.add(SymbolType.WORD); - return new SymbolTypeMulti(types); - } else if((list.getList().size() == 2 && SymbolType.WORD.equals(elmType))) { - // Potentially a dword constructor - return a composite type - ArrayList types = new ArrayList<>(); - types.add(new SymbolTypeArray(elmType)); - types.add(SymbolType.DWORD); - return new SymbolTypeMulti(types); - } else { - return new SymbolTypeArray(elmType); - } + long size = list.getList().size(); + return new SymbolTypeArray(elmType, new ConstantInteger(size, size<256?SymbolType.BYTE:SymbolType.WORD)); } else { throw new RuntimeException("Cannot infer list element type " + list.toString()); } @@ -223,13 +206,8 @@ public class SymbolTypeInference { SymbolType valueType = inferType(programScope, rValue); if(type == null) { type = valueType; - } else { - SymbolType newType = intersectTypes(type, valueType); - if(newType == null) { - throw new CompileError("Types not compatible " + type + " and " + valueType); - } - type = newType; - } + } else if(!type.equals(valueType)) + throw new CompileError("Types not compatible " + type + " and " + valueType); } if(!SymbolType.VAR.equals(symbol.getType()) && !type.equals(symbol.getType())) { program.getLog().append("Inferred type updated to " + type + " for " + symbol.toString(program)); @@ -295,53 +273,4 @@ public class SymbolTypeInference { } } - /** - * Find the symbol type that is the intersection between the two passed types. - * Handles SymbolTypeMulti by intersecting the sub type lists. - * - * @param type1 The first type - * @param type2 The second type - * @return The intersection between the two types (handling multi-types) - */ - public static SymbolType intersectTypes(SymbolType type1, SymbolType type2) { - List newSubTypes = new ArrayList<>(); - if(type1 instanceof SymbolTypeMulti) { - Collection subTypes1 = ((SymbolTypeMulti) type1).getTypes(); - if(type2 instanceof SymbolTypeMulti) { - Collection subTypes2 = ((SymbolTypeMulti) type2).getTypes(); - for(SymbolType subType1 : subTypes1) { - if(subTypes2.contains(subType1)) { - newSubTypes.add(subType1); - } - } - } else { - // Element type is not multi - check if the list type contains it - if(subTypes1.contains(type2)) { - newSubTypes.add(type2); - } - } - } else { - // List-type not multi - check if the element type contains it - if(type2 instanceof SymbolTypeMulti) { - Collection subTypes2 = ((SymbolTypeMulti) type2).getTypes(); - if(subTypes2.contains(type1)) { - newSubTypes.add(type1); - } - } else { - // Element type is not multi - check if the list type is the same - if(type1.equals(type2)) { - newSubTypes.add(type1); - } - } - } - if(newSubTypes.size() == 0) { - return null; - } else if(newSubTypes.size() == 1) { - // A single type matching - use it - return newSubTypes.get(0); - } else { - // Multiple matches was found - use them - return new SymbolTypeMulti(newSubTypes); - } - } } diff --git a/src/main/java/dk/camelot64/kickc/model/values/ValueList.java b/src/main/java/dk/camelot64/kickc/model/values/ValueList.java index fd704486a..a81adb58a 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ValueList.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ValueList.java @@ -38,4 +38,8 @@ public class ValueList implements RValue { return out.toString(); } + @Override + public String toString() { + return toString(null); + } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantCastSimplification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantCastSimplification.java index 0d516f99e..467e386ca 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantCastSimplification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantCastSimplification.java @@ -53,37 +53,6 @@ public class Pass2ConstantCastSimplification extends Pass2SsaOptimization { } } }); - ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> { - if(programValue.get() instanceof ConstantCastValue) { - ConstantCastValue constantCastValue = (ConstantCastValue) programValue.get(); - if(constantCastValue.getValue() instanceof ConstantInteger) { - ConstantInteger constantInteger = (ConstantInteger) constantCastValue.getValue(); - SymbolType castType = constantCastValue.getToType(); - if(SymbolType.NUMBER.equals(constantInteger.getType())) { - if(castType instanceof SymbolTypeIntegerFixed ) { - ConstantInteger newConstInt = new ConstantInteger(constantInteger.getInteger(), castType); - programValue.set(newConstInt); - getLog().append("Simplifying constant integer cast " + newConstInt.toString()); - optimized.set(true); - } else if(castType instanceof SymbolTypePointer) { - ConstantPointer newConstPointer = new ConstantPointer(constantInteger.getInteger(), ((SymbolTypePointer) castType).getElementType()); - programValue.set(newConstPointer); - getLog().append("Simplifying constant pointer cast " + newConstPointer.toString()); - optimized.set(true); - } - } else if(castType instanceof SymbolTypeIntegerFixed) { - if(((SymbolTypeIntegerFixed) castType).contains(constantInteger.getValue())) { - // Cast type contains the value - cast not needed - ConstantInteger newConstInt = new ConstantInteger(constantInteger.getInteger(), castType); - programValue.set(newConstInt); - getLog().append("Simplifying constant integer cast " + newConstInt.toString()); - optimized.set(true); - } - } - } - } - - }); return optimized.get(); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructorsNew.java b/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructorsNew.java new file mode 100644 index 000000000..edd754321 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructorsNew.java @@ -0,0 +1,69 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.Comment; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.iterator.ProgramExpressionIterator; +import dk.camelot64.kickc.model.iterator.ProgramExpressionUnary; +import dk.camelot64.kickc.model.operators.OperatorBinary; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.Scope; +import dk.camelot64.kickc.model.symbols.VariableIntermediate; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.CastValue; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.ValueList; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Identifies word constructors (word) { b1, b2 } and replaces + * them with a binary operator word w = b1 w= b2 ; + */ +public class Pass2FixInlineConstructorsNew extends Pass2SsaOptimization { + + public Pass2FixInlineConstructorsNew(Program program) { + super(program); + } + + @Override + public boolean step() { + AtomicBoolean optimized = new AtomicBoolean(false); + ProgramExpressionIterator.execute(getProgram(), (programExpression, currentStmt, stmtIt, currentBlock) -> { + if(programExpression instanceof ProgramExpressionUnary) { + if(programExpression.getOperator().equals(Operators.CAST_WORD)) { + if(((ProgramExpressionUnary) programExpression).getOperand() instanceof ValueList) { + ValueList list = (ValueList) ((ProgramExpressionUnary) programExpression).getOperand(); + List listValues = list.getList(); + if(listValues.size()==2) { + + OperatorBinary constructOperator = Operators.WORD; + SymbolType constructType = SymbolType.WORD; + + // Convert list to a word constructor in a new tmp variable + Scope currentScope = Pass2FixInlineConstructorsNew.this.getScope().getScope(currentBlock.getScope()); + VariableIntermediate tmpVar = currentScope.addVariableIntermediate(); + tmpVar.setTypeInferred(constructType); + // Move backward - to insert before the current statement + stmtIt.previous(); + // Add assignment of the new tmpVar + StatementAssignment assignment = new StatementAssignment(tmpVar.getRef(), new CastValue(SymbolType.BYTE, listValues.get(0)), constructOperator, new CastValue(SymbolType.BYTE, listValues.get(1)), currentStmt.getSource(), Comment.NO_COMMENTS); + stmtIt.add(assignment); + // Move back before the current statement + stmtIt.next(); + // Replace current value with the reference + programExpression.set(tmpVar.getRef()); + Pass2FixInlineConstructorsNew.this.getLog().append("Fixing inline constructor with " + assignment.toString()); + optimized.set(true); + } + } + } + } + }); + return optimized.get(); + + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java index e2f2049c0..908cb0587 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java @@ -4,6 +4,7 @@ import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramExpressionBinary; import dk.camelot64.kickc.model.iterator.ProgramExpressionIterator; import dk.camelot64.kickc.model.types.*; +import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.RValue; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,6 +38,15 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization { binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getScope()); modified.set(true); } + + // Detect word literal constructor + if(leftType.equals(SymbolType.WORD) && isLiteralWordCandidate(rightType)) { + SymbolType conversionType = SymbolType.WORD; + getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false))); + binary.addRightCast(SymbolType.WORD, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getScope()); + modified.set(true); + } + } if(leftType instanceof SymbolTypeIntegerFixed && SymbolType.isInteger(rightType)) { SymbolType conversionType = SymbolTypeConversion.convertedMathType((SymbolTypeInteger) leftType, (SymbolTypeInteger) rightType); @@ -53,5 +63,14 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization { return modified.get(); } + public static boolean isLiteralWordCandidate(SymbolType rightType) { + if(rightType instanceof SymbolTypeArray) { + SymbolTypeArray rightArray = (SymbolTypeArray) rightType; + if(new ConstantInteger(2L, SymbolType.BYTE).equals(rightArray.getSize())) + if(SymbolType.BYTE.equals(rightArray.getElementType()) || SymbolType.NUMBER.equals(rightArray.getElementType())) + return true; + } + return false; + } } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 100a0e868..3bb2350d3 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -1359,6 +1359,21 @@ public class TestPrograms { compileAndCompare("inline-assignment"); } + @Test + public void testInlineWord0() throws IOException, URISyntaxException { + compileAndCompare("inline-word-0"); + } + + @Test + public void testInlineWord1() throws IOException, URISyntaxException { + compileAndCompare("inline-word-1"); + } + + @Test + public void testInlineWord2() throws IOException, URISyntaxException { + compileAndCompare("inline-word-2"); + } + @Test public void testInlineWord() throws IOException, URISyntaxException { compileAndCompare("inline-word"); diff --git a/src/test/kc/inline-word-0.kc b/src/test/kc/inline-word-0.kc new file mode 100644 index 000000000..e4ef83c7c --- /dev/null +++ b/src/test/kc/inline-word-0.kc @@ -0,0 +1,7 @@ +// Tests minimal inline word + +void main() { + word w = { 0x02ub, 0x01ub }; + word* screen = 0x0400; + screen[0] = w; +} \ No newline at end of file diff --git a/src/test/kc/inline-word-1.kc b/src/test/kc/inline-word-1.kc new file mode 100644 index 000000000..9154746f8 --- /dev/null +++ b/src/test/kc/inline-word-1.kc @@ -0,0 +1,7 @@ +// Tests minimal inline word + +void main() { + word w = { 0x01, 0x02 }; + word* screen = 0x0400; + screen[0] = w; +} \ No newline at end of file diff --git a/src/test/kc/inline-word-2.kc b/src/test/kc/inline-word-2.kc new file mode 100644 index 000000000..9cff6751a --- /dev/null +++ b/src/test/kc/inline-word-2.kc @@ -0,0 +1,7 @@ +// Tests minimal inline word + +void main() { + word w = { 0x01, 0x02ub }; + word* screen = 0x0400; + screen[0] = w; +} \ No newline at end of file