diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index cb8d88400..f9a6b1fc6 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -153,7 +153,8 @@ public class Compiler { new Pass1GenerateControlFlowGraph(program).execute(); new Pass1ResolveForwardReferences(program).execute(); new Pass1UnwindBlockScopes(program).execute(); - new Pass1TypeInference(program).execute(); + new Pass1Procedures(program).execute(); + new PassNTypeInference(program).execute(); new Pass1TypeIdSimplification(program).execute(); if(getLog().isVerbosePass1CreateSsa()) { @@ -164,7 +165,7 @@ public class Compiler { new Pass1FixLValuesLoHi(program).execute(); new Pass1AssertNoLValueIntermediate(program).execute(); new Pass1PointerSizeofFix(program).execute(); - new Pass1AddTypePromotions(program).execute(); + new PassNAddTypeConversions(program).execute(); new Pass1EarlyConstantIdentification(program).execute(); new PassNStatementIndices(program).step(); new PassNCallGraphAnalysis(program).step(); @@ -230,6 +231,8 @@ public class Compiler { private void pass2Optimize() { List optimizations = new ArrayList<>(); + optimizations.add(new PassNTypeInference(program)); + optimizations.add(new PassNAddTypeConversions(program)); optimizations.add(new Pass2CullEmptyBlocks(program)); optimizations.add(new PassNStatementIndices(program)); optimizations.add(new PassNVariableReferenceInfos(program)); @@ -240,6 +243,7 @@ public class Compiler { optimizations.add(new Pass2IdenticalPhiElimination(program)); optimizations.add(new Pass2ConditionalJumpSimplification(program)); optimizations.add(new Pass2ConditionalAndOrRewriting(program)); + optimizations.add(new Pass2ConstantRValueConsolidation(program)); optimizations.add(new Pass2ConstantIdentification(program)); optimizations.add(new PassNStatementIndices(program)); optimizations.add(new PassNVariableReferenceInfos(program)); @@ -294,6 +298,7 @@ public class Compiler { constantOptimizations.add(new Pass2ConstantInlining(program)); constantOptimizations.add(new Pass2ConstantStringConsolidation(program)); constantOptimizations.add(new Pass2IdenticalPhiElimination(program)); + constantOptimizations.add(new Pass2ConstantRValueConsolidation(program)); constantOptimizations.add(new Pass2ConstantIdentification(program)); constantOptimizations.add(new Pass2ConstantAdditionElimination(program)); constantOptimizations.add(new Pass2ConstantSimplification(program)); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java index 28f28111f..895e806a8 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java @@ -7,11 +7,11 @@ import dk.camelot64.kickc.model.symbols.ConstantVar; import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Symbol; import dk.camelot64.kickc.model.symbols.Variable; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.types.*; import dk.camelot64.kickc.model.values.*; +import java.util.List; + /** Formatting of numbers, constants, names and more for KickAssembler */ public class AsmFormat { @@ -61,7 +61,7 @@ public class AsmFormat { } else if(symbol instanceof Procedure) { return getAsmParamName((Procedure) symbol, codeScope); } else { - throw new RuntimeException("Unhandled symbol type "+symbol); + throw new RuntimeException("Unhandled symbol type " + symbol); } } else if(value instanceof ConstantCastValue) { ConstantCastValue castValue = (ConstantCastValue) value; @@ -75,6 +75,7 @@ public class AsmFormat { /** * Get ASM for a binary constant expression + * * @param program The program * @param left The left operand of the expression * @param operator The binary operator @@ -85,12 +86,12 @@ public class AsmFormat { private static String getAsmConstantBinary(Program program, ConstantValue left, OperatorBinary operator, ConstantValue right, ScopeRef codeScope) { if(Operators.MODULO.equals(operator)) { // Remainder operator % not supported by KickAss - use modulo function instead - return "mod("+ + return "mod(" + getAsmConstant(program, left, operator.getPrecedence(), codeScope) + "," + - getAsmConstant(program, right, operator.getPrecedence(), codeScope)+ + getAsmConstant(program, right, operator.getPrecedence(), codeScope) + ")"; - } else { + } else { return getAsmConstant(program, left, operator.getPrecedence(), codeScope) + operator.getOperator() + getAsmConstant(program, right, operator.getPrecedence(), codeScope); @@ -109,28 +110,49 @@ public class AsmFormat { private static String getAsmConstantUnary(Program program, ScopeRef codeScope, Operator operator, ConstantValue operand, int outerPrecedence) { if(Operators.CAST_BYTE.equals(operator) || Operators.CAST_SBYTE.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); - if(SymbolType.isByte(operandType) || SymbolType.isSByte(operandType)) { + if(SymbolType.BYTE.equals(operandType) || SymbolType.SBYTE.equals(operandType)) { // No cast needed return getAsmConstant(program, operand, outerPrecedence, codeScope); - } else { - return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long)0xff), Operators.BOOL_AND, operand), outerPrecedence, codeScope); } - } else if(operator instanceof OperatorCastPtr || Operators.CAST_WORD.equals(operator) || Operators.CAST_SWORD.equals(operator) ) { + if(SymbolType.NUMBER.equals(operandType)) { + List operandInferedTypes = SymbolTypeNumberInference.inferTypes(program.getScope(), operand); + if(operandInferedTypes.contains(SymbolType.BYTE) || operandInferedTypes.contains(SymbolType.SBYTE)) { + // No cast needed + return getAsmConstant(program, operand, outerPrecedence, codeScope); + } + } + // Cast is needed + return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) 0xff), Operators.BOOL_AND, operand), outerPrecedence, codeScope); + } else if(operator instanceof OperatorCastPtr || Operators.CAST_WORD.equals(operator) || Operators.CAST_SWORD.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); - if(SymbolType.isWord(operandType) || SymbolType.isSWord(operandType) || SymbolType.isByte(operandType) || SymbolType.isSByte(operandType) || operandType instanceof SymbolTypePointer) { + if(SymbolType.WORD.equals(operandType) || SymbolType.SWORD.equals(operandType) || SymbolType.BYTE.equals(operandType) || SymbolType.SBYTE.equals(operandType) || operandType instanceof SymbolTypePointer) { // No cast needed return getAsmConstant(program, operand, outerPrecedence, codeScope); - } else { - return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long)0xffff), Operators.BOOL_AND, operand), outerPrecedence, codeScope); } + if(SymbolType.NUMBER.equals(operandType)) { + List operandInferedTypes = SymbolTypeNumberInference.inferTypes(program.getScope(), operand); + if(operandInferedTypes.contains(SymbolType.WORD) || operandInferedTypes.contains(SymbolType.SWORD)) { + // No cast needed + return getAsmConstant(program, operand, outerPrecedence, codeScope); + } + } + // Cast is needed + return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) 0xffff), Operators.BOOL_AND, operand), outerPrecedence, codeScope); } else if(Operators.CAST_DWORD.equals(operator) || Operators.CAST_SDWORD.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); - if(SymbolType.isDWord(operandType) || SymbolType.isSDWord(operandType)) { + if(SymbolType.DWORD.equals(operandType) || SymbolType.SDWORD.equals(operandType) || SymbolType.WORD.equals(operandType) || SymbolType.SWORD.equals(operandType) || SymbolType.BYTE.equals(operandType) || SymbolType.SBYTE.equals(operandType) || operandType instanceof SymbolTypePointer) { // No cast needed return getAsmConstant(program, operand, outerPrecedence, codeScope); - } else { - return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long)0xffffffffL), Operators.BOOL_AND, operand), outerPrecedence, codeScope); } + if(SymbolType.NUMBER.equals(operandType)) { + List operandInferedTypes = SymbolTypeNumberInference.inferTypes(program.getScope(), operand); + if(operandInferedTypes.contains(SymbolType.DWORD) || operandInferedTypes.contains(SymbolType.SDWORD)) { + // No cast needed + return getAsmConstant(program, operand, outerPrecedence, codeScope); + } + } + // Cast is needed + return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) 0xffffffffL), Operators.BOOL_AND, operand), outerPrecedence, codeScope); } else if(Operators.LOWBYTE.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); if(SymbolType.isByte(operandType) || SymbolType.isSByte(operandType)) { @@ -138,9 +160,9 @@ public class AsmFormat { } else if(SymbolType.isWord(operandType) || SymbolType.isSWord(operandType) || operandType instanceof SymbolTypePointer || SymbolType.STRING.equals(operandType)) { return "<" + getAsmConstant(program, operand, outerPrecedence, codeScope); } else if(SymbolType.isDWord(operandType) || SymbolType.isSDWord(operandType)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_AND, new ConstantInteger((long)0xffff)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_AND, new ConstantInteger((long) 0xffff)), outerPrecedence, codeScope); } else { - throw new CompileError("Unhandled type "+operand); + throw new CompileError("Unhandled type " + operand); } } else if(Operators.HIBYTE.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); @@ -149,24 +171,24 @@ public class AsmFormat { } else if(SymbolType.isWord(operandType) || SymbolType.isSWord(operandType) || operandType instanceof SymbolTypePointer || SymbolType.STRING.equals(operandType)) { return ">" + getAsmConstant(program, operand, outerPrecedence, codeScope); } else if(SymbolType.isDWord(operandType) || SymbolType.isSDWord(operandType)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.SHIFT_RIGHT, new ConstantInteger((long)16)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.SHIFT_RIGHT, new ConstantInteger((long) 16)), outerPrecedence, codeScope); } else { - throw new CompileError("Unhandled type "+operand); + throw new CompileError("Unhandled type " + operand); } } else if(Operators.INCREMENT.equals(operator)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.PLUS, new ConstantInteger((long)1)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.PLUS, new ConstantInteger((long) 1)), outerPrecedence, codeScope); } else if(Operators.DECREMENT.equals(operator)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.MINUS, new ConstantInteger((long)1)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.MINUS, new ConstantInteger((long) 1)), outerPrecedence, codeScope); } else if(Operators.BOOL_NOT.equals(operator)) { SymbolType operandType = SymbolTypeInference.inferType(program.getScope(), operand); if(SymbolType.isByte(operandType) || SymbolType.isSByte(operandType)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long)0xff)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long) 0xff)), outerPrecedence, codeScope); } else if(SymbolType.isWord(operandType) || SymbolType.isSWord(operandType) || operandType instanceof SymbolTypePointer || SymbolType.STRING.equals(operandType)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long)0xffff)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long) 0xffff)), outerPrecedence, codeScope); } else if(SymbolType.isDWord(operandType) || SymbolType.isSDWord(operandType)) { - return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long)0xffffffff)), outerPrecedence, codeScope); + return getAsmConstant(program, new ConstantBinary(operand, Operators.BOOL_XOR, new ConstantInteger((long) 0xffffffff)), outerPrecedence, codeScope); } else { - throw new CompileError("Unhandled type "+operand); + throw new CompileError("Unhandled type " + operand); } } else { return operator.getOperator() + @@ -208,6 +230,7 @@ public class AsmFormat { /** * Get the ASM code for a boolean value + * * @param bool the boolean vallue * @return "0" / "1" */ diff --git a/src/main/java/dk/camelot64/kickc/model/InternalError.java b/src/main/java/dk/camelot64/kickc/model/InternalError.java new file mode 100644 index 000000000..abf3ec469 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/InternalError.java @@ -0,0 +1,24 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementSource; + +/** Signals an internal error in the compiler. Should be reported to the author. */ +public class InternalError extends RuntimeException { + + public InternalError(String message) { + super(message); + } + + public InternalError(String message, StatementSource source) { + super(message+"\n"+source.toString()); + } + + public InternalError(String message, Statement statement) { + this(message, statement.getSource()); + } + + public InternalError(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java index af0323d3e..022611a5b 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java @@ -32,16 +32,9 @@ public class OperatorBitwiseAnd extends OperatorBinary { if(type2 instanceof SymbolTypePointer) { type2 = SymbolType.WORD; } - // Find smallest bitwise type - if(type1 instanceof SymbolTypeInteger && type2 instanceof SymbolTypeInteger) { - - for(SymbolTypeInteger candidate : SymbolType.getIntegerTypes()) { - boolean match1 = ((SymbolTypeInteger) type1).getBits() <= candidate.getBits(); - boolean match2 = ((SymbolTypeInteger) type2).getBits() <= candidate.getBits(); - if(!candidate.isSigned() && (match1 || match2)) { - return candidate; - } - } + // Handle numeric types + if(SymbolType.isInteger(type1) && SymbolType.isInteger(type2)) { + return SymbolType.convertedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); } throw new CompileError("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java index 5b7669cf1..cad932fb1 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java @@ -1,10 +1,7 @@ package dk.camelot64.kickc.model.operators; import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; -import dk.camelot64.kickc.model.types.SymbolTypePointer; -import dk.camelot64.kickc.model.types.SymbolTypeSimple; +import dk.camelot64.kickc.model.types.*; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; @@ -32,9 +29,9 @@ public class OperatorBitwiseOr extends OperatorBinary { if(type2 instanceof SymbolTypePointer) { type2 = SymbolType.WORD; } - // Handle numeric types through proper promotion + // Handle numeric types if(SymbolType.isInteger(type1) && SymbolType.isInteger(type2)) { - return SymbolType.promotedBitwiseType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); + return SymbolType.convertedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); } throw new CompileError("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java index f829640ef..b383ac3f0 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java @@ -1,10 +1,7 @@ package dk.camelot64.kickc.model.operators; import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; -import dk.camelot64.kickc.model.types.SymbolTypePointer; -import dk.camelot64.kickc.model.types.SymbolTypeSimple; +import dk.camelot64.kickc.model.types.*; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; @@ -32,9 +29,9 @@ public class OperatorBitwiseXor extends OperatorBinary { if(type2 instanceof SymbolTypePointer) { type2 = SymbolType.WORD; } - // Handle numeric types through proper promotion + // Handle numeric types if(SymbolType.isInteger(type1) && SymbolType.isInteger(type2)) { - return SymbolType.promotedBitwiseType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); + return SymbolType.convertedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); } throw new CompileError("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastByte.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastByte.java index de2a37f71..ea24ee803 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastByte.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastByte.java @@ -18,9 +18,9 @@ public class OperatorCastByte extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xff & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xff & ((ConstantInteger) value).getValue(), SymbolType.BYTE); } else if(value instanceof ConstantPointer) { - return new ConstantInteger(0xff & ((ConstantPointer) value).getLocation()); + return new ConstantInteger(0xff & ((ConstantPointer) value).getLocation(), SymbolType.BYTE); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastDWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastDWord.java index f27ae69e2..af76dbff4 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastDWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastDWord.java @@ -18,10 +18,10 @@ public class OperatorCastDWord extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xffffffffL & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xffffffffL & ((ConstantInteger) value).getValue(), SymbolType.DWORD); } if(value instanceof ConstantPointer) { - return new ConstantInteger(0xffff & ((ConstantPointer) value).getLocation()); + return new ConstantInteger(0xffff & ((ConstantPointer) value).getLocation(), SymbolType.DWORD); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSByte.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSByte.java index 8013ce770..d81cf7414 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSByte.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSByte.java @@ -17,7 +17,7 @@ public class OperatorCastSByte extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xff & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xff & ((ConstantInteger) value).getValue(), SymbolType.SBYTE); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSDWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSDWord.java index 01f5137bf..a07c5cd38 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSDWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSDWord.java @@ -17,7 +17,7 @@ public class OperatorCastSDWord extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xffffffffL & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xffffffffL & ((ConstantInteger) value).getValue(), SymbolType.SDWORD); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSWord.java index ca7f35d83..094be277d 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastSWord.java @@ -17,7 +17,7 @@ public class OperatorCastSWord extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xffff & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xffff & ((ConstantInteger) value).getValue(), SymbolType.SWORD); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastWord.java index 6414be198..f84aec9f0 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorCastWord.java @@ -18,10 +18,10 @@ public class OperatorCastWord extends OperatorUnary { @Override public ConstantLiteral calculateLiteral(ConstantLiteral value, ProgramScope scope) { if(value instanceof ConstantInteger) { - return new ConstantInteger(0xffff & ((ConstantInteger) value).getValue()); + return new ConstantInteger(0xffff & ((ConstantInteger) value).getValue(), SymbolType.WORD); } if(value instanceof ConstantPointer) { - return new ConstantInteger(0xffff & ((ConstantPointer) value).getLocation()); + return new ConstantInteger(0xffff & ((ConstantPointer) value).getLocation(), SymbolType.WORD); } throw new CompileError("Calculation not implemented " + getOperator() + " " + value ); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java index 1e1ce9cd6..2a4058f16 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java @@ -38,7 +38,7 @@ public class OperatorDivide extends OperatorBinary { } // Handle numeric types through proper promotion if(SymbolType.isInteger(left) && SymbolType.isInteger(right)) { - return SymbolType.promotedMathType((SymbolTypeInteger) left, (SymbolTypeInteger) right); + return SymbolType.convertedMathType( (SymbolTypeInteger) left, (SymbolTypeInteger)right); } throw new RuntimeException("Type inference case not handled " + left + " " + getOperator() + " " + right); diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java index 3f018c9d5..c06f6b938 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java @@ -1,10 +1,7 @@ package dk.camelot64.kickc.model.operators; import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; -import dk.camelot64.kickc.model.types.SymbolTypePointer; -import dk.camelot64.kickc.model.types.SymbolTypeSimple; +import dk.camelot64.kickc.model.types.*; import dk.camelot64.kickc.model.values.ConstantChar; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; @@ -39,10 +36,9 @@ public class OperatorMinus extends OperatorBinary { } else if(type1 instanceof SymbolTypePointer && type2 instanceof SymbolTypePointer) { return SymbolType.WORD; } - // Handle numeric types through proper promotion if(SymbolType.isInteger(type1) && SymbolType.isInteger(type2)) { - return SymbolType.promotedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); + return SymbolType.convertedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); } throw new RuntimeException("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java index c26e36089..2931f6db1 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java @@ -27,7 +27,7 @@ public class OperatorModulo extends OperatorBinary { public SymbolType inferType(SymbolTypeSimple left, SymbolTypeSimple right) { // Handle numeric types through proper promotion if(SymbolType.isInteger(left) && SymbolType.isInteger(right)) { - return SymbolType.promotedMathType((SymbolTypeInteger) left, (SymbolTypeInteger) right); + return SymbolType.convertedMathType((SymbolTypeInteger) left, (SymbolTypeInteger) right); } if(left instanceof ConstantPointer && right instanceof ConstantInteger) { return ((ConstantInteger) right).getType(); diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java index 5da795636..7c3894bb5 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java @@ -3,6 +3,7 @@ package dk.camelot64.kickc.model.operators; import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeInteger; +import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed; import dk.camelot64.kickc.model.types.SymbolTypeSimple; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; @@ -26,7 +27,7 @@ public class OperatorMultiply extends OperatorBinary { public SymbolType inferType(SymbolTypeSimple left, SymbolTypeSimple right) { // Handle numeric types through proper promotion if(SymbolType.isInteger(left) && SymbolType.isInteger(right)) { - return SymbolType.promotedMathType((SymbolTypeInteger) left, (SymbolTypeInteger) right); + return SymbolType.convertedMathType((SymbolTypeInteger) left, (SymbolTypeInteger) right); } throw new RuntimeException("Type inference case not handled " + left + " " + getOperator() + " " + right); } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java index 99556aa2e..f4f6e0ec7 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java @@ -39,31 +39,14 @@ public class OperatorPlus extends OperatorBinary { @Override public SymbolType inferType(SymbolTypeSimple type1, SymbolTypeSimple type2) { // Handle all non-numeric types - if(type1.equals(SymbolType.STRING) && isStringLike(type2)) { - return SymbolType.STRING; - } else if(isStringLike(type1) && type2.equals(SymbolType.STRING)) { - return SymbolType.STRING; - } else if(type1 instanceof SymbolTypeArray && type2 instanceof SymbolTypeArray) { - SymbolType elemType1 = ((SymbolTypeArray) type1).getElementType(); - SymbolType elemType2 = ((SymbolTypeArray) type2).getElementType(); - if(SymbolTypeInference.typeMatch(elemType1, elemType2)) { - return new SymbolTypeArray(elemType1); - } else if(SymbolTypeInference.typeMatch(elemType2, elemType1)) { - return new SymbolTypeArray(elemType2); - } else { - throw new RuntimeException("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); - } - } else if(SymbolType.isInteger(type1) && type2 instanceof SymbolTypePointer ) { + if(SymbolType.isInteger(type1) && type2 instanceof SymbolTypePointer) { return new SymbolTypePointer(((SymbolTypePointer) type2).getElementType()); } else if(type1 instanceof SymbolTypePointer && SymbolType.isInteger(type2)) { return new SymbolTypePointer(((SymbolTypePointer) type1).getElementType()); - } else if(type1 instanceof SymbolTypePointer && type2 instanceof SymbolTypePointer) { - throw new NoMatchingType("Two pointers cannot be added."); } - // Handle numeric types through proper promotion if(SymbolType.isInteger(type1) && SymbolType.isInteger(type2)) { - return SymbolType.promotedMathType((SymbolTypeInteger) type1, (SymbolTypeInteger) type2); + return SymbolType.convertedMathType( (SymbolTypeInteger) type1, (SymbolTypeInteger)type2); } throw new CompileError("Type inference case not handled " + type1 + " " + getOperator() + " " + type2); diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolType.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolType.java index 9bf23a2f8..c8a043c17 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolType.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolType.java @@ -9,17 +9,19 @@ import java.util.Collection; public interface SymbolType { /** Unsigned byte (8 bits)). */ - SymbolTypeInteger BYTE = new SymbolTypeInteger("byte", 0, 255, false, 8); + SymbolTypeIntegerFixed BYTE = new SymbolTypeIntegerFixed("byte", 0, 255, false, 8); /** Signed byte (8 bits). */ - SymbolTypeInteger SBYTE = new SymbolTypeInteger("signed byte", -128, 127, true, 8); + SymbolTypeIntegerFixed SBYTE = new SymbolTypeIntegerFixed("signed byte", -128, 127, true, 8); /** Unsigned word (2 bytes, 16 bits). */ - SymbolTypeInteger WORD = new SymbolTypeInteger("word", 0, 65_535, false, 16); + SymbolTypeIntegerFixed WORD = new SymbolTypeIntegerFixed("word", 0, 65_535, false, 16); /** Signed word (2 bytes, 16 bits). */ - SymbolTypeInteger SWORD = new SymbolTypeInteger("signed word", -32_768, 32_767, true, 16); + SymbolTypeIntegerFixed SWORD = new SymbolTypeIntegerFixed("signed word", -32_768, 32_767, true, 16); /** Unsigned double word (4 bytes, 32 bits). */ - SymbolTypeInteger DWORD = new SymbolTypeInteger("dword", 0, 4_294_967_296L, false, 32); + SymbolTypeIntegerFixed DWORD = new SymbolTypeIntegerFixed("dword", 0, 4_294_967_296L, false, 32); /** Signed double word (4 bytes, 32 bits). */ - SymbolTypeInteger SDWORD = new SymbolTypeInteger("signed dword", -2_147_483_648, 2_147_483_647, true, 32); + SymbolTypeIntegerFixed SDWORD = new SymbolTypeIntegerFixed("signed dword", -2_147_483_648, 2_147_483_647, true, 32); + /** Integer with unknown size. */ + SymbolTypeIntegerAuto NUMBER = new SymbolTypeIntegerAuto("number"); /** String value (treated like byte* ). */ SymbolTypeNamed STRING = new SymbolTypeNamed("string", 99); /** Boolean value. */ @@ -204,13 +206,13 @@ public interface SymbolType { } /** - * Is the type an integer type or compatible {@link SymbolTypeMulti} + * Is the type an integer type (including {@link #NUMBER}) * * @param type The type to examine * @return true if the type is integer */ static boolean isInteger(SymbolType type) { - return isSDWord(type) || isDWord(type) || isSWord(type) || isWord(type) || isSByte(type) || isByte(type); + return SDWORD.equals(type) || DWORD.equals(type) || SWORD.equals(type) || WORD.equals(type) || SBYTE.equals(type) || BYTE.equals(type) || NUMBER.equals(type); } /** @@ -218,8 +220,8 @@ public interface SymbolType { * * @return All integeer types */ - static Collection getIntegerTypes() { - ArrayList types = new ArrayList<>(); + static Collection getIntegerFixedTypes() { + ArrayList types = new ArrayList<>(); types.add(BYTE); types.add(SBYTE); types.add(WORD); @@ -230,21 +232,41 @@ public interface SymbolType { } /** - * Find the smallest integer type that contains both sub-types usable for math ( + - * / ). + * Find the integer type that results from a binary operator according to C99 6.3.1.8 + * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=70 * * @param type1 Left type in a binary expression * @param type2 Right type in a binary expression - * @return + * @return The type resulting from a binary operator performed on the two parameters */ - static SymbolType promotedMathType(SymbolTypeInteger type1, SymbolTypeInteger type2) { - for(SymbolTypeInteger candidate : getIntegerTypes()) { - boolean match1 = type1.getMinValue() >= candidate.getMinValue() && type1.getMaxValue() <= candidate.getMaxValue(); - boolean match2 = type2.getMinValue() >= candidate.getMinValue() && type2.getMaxValue() <= candidate.getMaxValue(); - if(match1 && match2) { - return candidate; - } + static SymbolType convertedMathType(SymbolTypeInteger type1, SymbolTypeInteger type2) { + if(SymbolType.NUMBER.equals(type1) || SymbolType.NUMBER.equals(type2)) { + return NUMBER; } - throw new NoMatchingType("Cannot promote to a common type for "+type1.toString()+" and "+type2.toString()); + SymbolTypeIntegerFixed fixed1 = (SymbolTypeIntegerFixed) type1; + SymbolTypeIntegerFixed fixed2 = (SymbolTypeIntegerFixed) type2; + // C99 6.3.1.8 a. If two operands have the same type no conversion is performed + if(type1.equals(type2)) + return type1; + // C99 6.3.1.8 b. If both are signed or both are unsigned then the smallest type is converted to the size of the large type (byte->word->sword, sbyte->sword->sdword) + if(fixed1.isSigned()==fixed2.isSigned()) + return (fixed1.getBits()>fixed2.getBits()) ? fixed1 : fixed2; + // C99 6.3.1.8 c. One is signed and one unsigned. + // If the signed type can contain all values of the unsigned type then the unsigned value is converted to the signed type. (byte->sword, byte->sdword, word->sdword). + SymbolTypeIntegerFixed typeS, typeU; + if(fixed1.isSigned()) { + typeS = fixed1; + typeU = fixed2; + } else { + typeS = fixed2; + typeU = fixed1; + } + if(typeS.getBits()>typeU.getBits()) + return typeS; + // C99 6.3.1.8 d. The unsigned type is the same size as or larger than the signed type. + // The signed value is first converted to the size of the unsigned type and then converted to unsigned changing the sign and the value + // (sbyte->byte, sbyte->word, sbyte->dword, sword->word, sword->dword, sdword->dword). + return typeU; } /** @@ -254,8 +276,8 @@ public interface SymbolType { * @param type2 Right type in a binary expression * @return */ - static SymbolType promotedBitwiseType(SymbolTypeInteger type1, SymbolTypeInteger type2) { - for(SymbolTypeInteger candidate : getIntegerTypes()) { + static SymbolType promotedBitwiseType(SymbolTypeIntegerFixed type1, SymbolTypeIntegerFixed type2) { + for(SymbolTypeIntegerFixed candidate : getIntegerFixedTypes()) { if(!candidate.isSigned() && type1.getBits()<=candidate.getBits() && type2.getBits()<=candidate.getBits()) { return candidate; } 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 d80e44138..477a34d28 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java @@ -1,18 +1,15 @@ package dk.camelot64.kickc.model.types; import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.ConstantNotLiteral; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.operators.Operator; import dk.camelot64.kickc.model.operators.OperatorBinary; import dk.camelot64.kickc.model.operators.OperatorUnary; -import dk.camelot64.kickc.model.operators.Operators; 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.Arrays; import java.util.Collection; import java.util.List; @@ -30,23 +27,6 @@ public class SymbolTypeInference { * @return The type of the resulting value */ public static SymbolType inferType(ProgramScope programScope, OperatorUnary operator, RValue rValue) { - if(Operators.TYPEID.equals(operator)) { - return SymbolType.BYTE; - } else if(Operators.SIZEOF.equals(operator)) { - return SymbolType.BYTE; - } - if(rValue instanceof ConstantValue) { - // Calculate resulting constant literal - try { - ConstantValue constRValue = (ConstantValue) rValue; - ConstantLiteral literalRValue = constRValue.calculateLiteral(programScope); - ConstantValue value = operator.calculateLiteral(literalRValue, programScope); - return value.getType(programScope); - } catch(ConstantNotLiteral e) { - // Value literal cannot be calculated - } - } - // Infer value type - and then infer operator result type SymbolType valueType = inferType(programScope, rValue); return inferType(operator, valueType); } @@ -62,19 +42,7 @@ public class SymbolTypeInference { if(operandType instanceof SymbolTypeSimple) { return operator.inferType((SymbolTypeSimple) operandType); } else { - // Infer all resulting types for each sub-type of the multi-type - ArrayList resultTypes = new ArrayList<>(); - for(SymbolType opType : ((SymbolTypeMulti) operandType).getTypes()) { - SymbolType resType = inferType(operator, opType); - if(!resultTypes.contains(resType)) { - resultTypes.add(resType); - } - } - if(resultTypes.size() == 1) { - return resultTypes.get(0); - } else { - return new SymbolTypeMulti(resultTypes); - } + throw new RuntimeException("Not implemented!"); } } @@ -88,18 +56,6 @@ public class SymbolTypeInference { * @return The type of the resulting value */ public static SymbolType inferType(ProgramScope programScope, RValue left, OperatorBinary operator, RValue right) { - if(left instanceof ConstantValue && right instanceof ConstantValue) { - // Calculate resulting constant literal - try { - ConstantValue value = operator.calculateLiteral( - ((ConstantValue) left).calculateLiteral(programScope), - ((ConstantValue) right).calculateLiteral(programScope) - ); - return value.getType(programScope); - } catch(ConstantNotLiteral e) { - // Value literal cannot be calculated - } - } SymbolType leftType = inferType(programScope, left); SymbolType rightType = inferType(programScope, right); return inferType(leftType, operator, rightType); @@ -109,32 +65,7 @@ public class SymbolTypeInference { if(left instanceof SymbolTypeSimple && right instanceof SymbolTypeSimple) { return operator.inferType((SymbolTypeSimple) left, (SymbolTypeSimple) right); } else { - // Infer all resulting types for each sub-type of the multi-type - if(left instanceof SymbolTypeSimple) { - left = new SymbolTypeMulti(Arrays.asList(left)); - } - if(right instanceof SymbolTypeSimple) { - right = new SymbolTypeMulti(Arrays.asList(right)); - } - ArrayList resultTypes = new ArrayList<>(); - for(SymbolType leftType : ((SymbolTypeMulti) left).getTypes()) { - for(SymbolType rightType : ((SymbolTypeMulti) right).getTypes()) { - SymbolType resType; - try { - resType = inferType(leftType, operator, rightType); - if(!resultTypes.contains(resType)) { - resultTypes.add(resType); - } - } catch(NoMatchingType e) { - // Cannot promote to common type - ignore! - } - } - } - if(resultTypes.size() == 1) { - return resultTypes.get(0); - } else { - return new SymbolTypeMulti(resultTypes); - } + throw new RuntimeException("Not implemented!"); } } @@ -209,13 +140,8 @@ public class SymbolTypeInference { if(elmType == null) { elmType = type; } else { - // element type already defined - check for a match - if(!typeMatch(elmType, type)) { - if(typeMatch(type, elmType)) { - elmType = type; - } else { - throw new RuntimeException("Array element has type mismatch " + elm.toString() + " not matching type " + elmType.getTypeName()); - } + if(!elmType.equals(type)) { + throw new RuntimeException("Array element has type mismatch " + elm.toString() + " not matching type " + elmType.getTypeName()); } } } @@ -256,74 +182,12 @@ public class SymbolTypeInference { return rValueType; } - /** - * Determine if lValue and rValue types match (the same types, not needing a cast). - * - * @param lValueType The lValue type - * @param rValueType The rvalue type - * @return true if the types match - */ - public static boolean typeMatch(SymbolType lValueType, SymbolType rValueType) { - if(lValueType.equals(rValueType)) { - // Types match directly - return true; - } else if(rValueType instanceof SymbolTypeMulti) { - Collection rTypes = ((SymbolTypeMulti) rValueType).getTypes(); - if(lValueType instanceof SymbolTypeMulti) { - // Both are inline types - RValue type must be superset of LValue - Collection lTypes = ((SymbolTypeMulti) lValueType).getTypes(); - return typeContainsMatchAll(lTypes, rTypes); - } else { - // Types match because the right side matches the left side - return typeContainsMatch(lValueType, rTypes); - } - } else if(lValueType instanceof SymbolTypePointer && rValueType instanceof SymbolTypePointer) { - return typeMatch( - ((SymbolTypePointer) lValueType).getElementType(), - ((SymbolTypePointer) rValueType).getElementType()); - } else if(lValueType instanceof SymbolTypePointer && SymbolType.STRING.equals(rValueType)) { - if(SymbolType.isByte(((SymbolTypePointer) lValueType).getElementType())) { - return true; - } - } else if(SymbolType.STRING.equals(lValueType) && rValueType instanceof SymbolTypePointer) { - if(SymbolType.isByte(((SymbolTypePointer) rValueType).getElementType())) { - return true; - } - } - return false; - } - - private static boolean typeContainsMatchAll(Collection lTypes, Collection rTypes) { - for(SymbolType lType : lTypes) { - if(!typeContainsMatch(lType, rTypes)) { - return false; - } - } - return true; - } - - /** - * Determine is a list of potential inferred types contains a match for another type - * - * @param lValueType The type (rValue) we want to find a match for in the list - * @param rTypes The list of inferred potential types - * @return true if the list has a match - */ - private static boolean typeContainsMatch(SymbolType lValueType, Collection rTypes) { - for(SymbolType rType : rTypes) { - if(typeMatch(lValueType, rType)) { - return true; - } - } - return false; - } - public static void inferCallLValue(Program program, StatementCall call, boolean reinfer) { ProgramScope programScope = program.getScope(); LValue lValue = call.getlValue(); if(lValue instanceof VariableRef) { Variable symbol = programScope.getVariable((VariableRef) lValue); - if(SymbolType.VAR.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { + if(SymbolType.VAR.equals(symbol.getType()) || SymbolType.NUMBER.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { Procedure procedure = programScope.getProcedure(call.getProcedure()); SymbolType type = procedure.getReturnType(); setInferedType(program, call, symbol, type); @@ -336,7 +200,7 @@ public class SymbolTypeInference { LValue lValue = call.getlValue(); if(lValue instanceof VariableRef) { Variable symbol = programScope.getVariable((VariableRef) lValue); - if(SymbolType.VAR.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { + if(SymbolType.VAR.equals(symbol.getType()) || SymbolType.NUMBER.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { SymbolType procedureType = inferType(programScope, call.getProcedure()); if(procedureType instanceof SymbolTypeProcedure) { SymbolType returnType = ((SymbolTypeProcedure) procedureType).getReturnType(); @@ -350,7 +214,7 @@ public class SymbolTypeInference { ProgramScope programScope = program.getScope(); Variable symbol = programScope.getVariable(phiVariable.getVariable()); - if(SymbolType.VAR.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { + if(SymbolType.VAR.equals(symbol.getType()) || SymbolType.NUMBER.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { SymbolType type = null; for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { RValue rValue = phiRValue.getrValue(); @@ -377,7 +241,7 @@ public class SymbolTypeInference { LValue lValue = assignment.getlValue(); if(lValue instanceof VariableRef) { Variable symbol = programScope.getVariable((VariableRef) lValue); - if(SymbolType.VAR.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { + if(SymbolType.VAR.equals(symbol.getType()) || SymbolType.NUMBER.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) { // Unresolved symbol - perform inference Operator operator = assignment.getOperator(); if(assignment.getrValue1() == null && operator == null) { diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInteger.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInteger.java index 1813650c8..2a4cdfa7f 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInteger.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInteger.java @@ -1,50 +1,6 @@ package dk.camelot64.kickc.model.types; -/** Integer symbol types (byte, signed byte, word, ...). */ -public class SymbolTypeInteger implements SymbolTypeSimple { +/** Integer type marker interface. */ +public interface SymbolTypeInteger extends SymbolTypeSimple { - private final String typeName; - private final long minValue; - private final long maxValue; - private final boolean signed; - private final int bits; - - SymbolTypeInteger(String typeName, long minValue, long maxValue, boolean signed, int bits) { - this.typeName = typeName; - this.minValue = minValue; - this.maxValue = maxValue; - this.signed = signed; - this.bits = bits; - } - - @Override - public String getTypeName() { - return typeName; - } - - public long getMinValue() { - return minValue; - } - - public long getMaxValue() { - return maxValue; - } - - public boolean isSigned() { - return signed; - } - - public int getBits() { - return bits; - } - - @Override - public int getSizeBytes() { - return bits/8; - } - - @Override - public String toString() { - return getTypeName(); - } } diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerAuto.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerAuto.java new file mode 100644 index 000000000..6e643aad8 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerAuto.java @@ -0,0 +1,42 @@ +package dk.camelot64.kickc.model.types; + +import java.util.Objects; + +/** Integer type that has not yet been fixed. This is used for constant expressions. The type is fixed when the constant meets a fixed type. */ +public class SymbolTypeIntegerAuto implements SymbolTypeInteger { + + private final String typeName; + + SymbolTypeIntegerAuto(String typeName) { + this.typeName = typeName; + } + + @Override + public String getTypeName() { + return typeName; + } + + @Override + public int getSizeBytes() { + return -1; + } + + @Override + public String toString() { + return getTypeName(); + } + + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + SymbolTypeIntegerAuto that = (SymbolTypeIntegerAuto) o; + return Objects.equals(typeName, that.typeName); + } + + @Override + public int hashCode() { + return Objects.hash(typeName); + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerFixed.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerFixed.java new file mode 100644 index 000000000..1c6a35cc5 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeIntegerFixed.java @@ -0,0 +1,50 @@ +package dk.camelot64.kickc.model.types; + +/** Integer type with a fixed size (byte, signed byte, word, ...). */ +public class SymbolTypeIntegerFixed implements SymbolTypeInteger { + + private final String typeName; + private final long minValue; + private final long maxValue; + private final boolean signed; + private final int bits; + + SymbolTypeIntegerFixed(String typeName, long minValue, long maxValue, boolean signed, int bits) { + this.typeName = typeName; + this.minValue = minValue; + this.maxValue = maxValue; + this.signed = signed; + this.bits = bits; + } + + @Override + public String getTypeName() { + return typeName; + } + + public long getMinValue() { + return minValue; + } + + public long getMaxValue() { + return maxValue; + } + + public boolean isSigned() { + return signed; + } + + public int getBits() { + return bits; + } + + @Override + public int getSizeBytes() { + return bits/8; + } + + @Override + public String toString() { + return getTypeName(); + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeMulti.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeMulti.java index 5b1ca45a5..ced732cb9 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeMulti.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeMulti.java @@ -34,7 +34,7 @@ public class SymbolTypeMulti implements SymbolType { */ public static SymbolType getMultiType(Long number) { ArrayList potentialTypes = new ArrayList<>(); - for(SymbolTypeInteger typeInteger : SymbolType.getIntegerTypes()) { + for(SymbolTypeIntegerFixed typeInteger : SymbolType.getIntegerFixedTypes()) { if(number >= typeInteger.getMinValue() && number <= typeInteger.getMaxValue()) { potentialTypes.add(typeInteger); } diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeNumberInference.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeNumberInference.java new file mode 100644 index 000000000..7473e4230 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeNumberInference.java @@ -0,0 +1,104 @@ +package dk.camelot64.kickc.model.types; + +import dk.camelot64.kickc.model.InternalError; +import dk.camelot64.kickc.model.operators.OperatorBinary; +import dk.camelot64.kickc.model.operators.OperatorUnary; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.values.ConstantInteger; +import dk.camelot64.kickc.model.values.ConstantLiteral; +import dk.camelot64.kickc.model.values.ConstantValue; +import dk.camelot64.kickc.model.values.RValue; + +import java.util.ArrayList; +import java.util.List; + +/** + * Interference of possible types for constant expressions with the {@link SymbolType#NUMBER} type. + * This is done by evaluating the constant expression to find the literal value. + */ +public class SymbolTypeNumberInference { + + /** + * Infer the potential types for an RValue with {@link SymbolType#NUMBER} type + * + * @return The potential types + */ + public static List inferTypesRValue(ProgramScope symbols, StatementAssignment assignment) { + RValue rValue1 = assignment.getrValue1(); + RValue rValue2 = assignment.getrValue2(); + if(assignment.getrValue1() == null && assignment.getOperator() == null && assignment.getrValue2() instanceof ConstantValue) { + return inferTypes(symbols, (ConstantValue) rValue2); + } else if(assignment.getrValue1() == null && assignment.getOperator() instanceof OperatorUnary && assignment.getrValue2() instanceof ConstantValue) { + return inferTypes(symbols, (OperatorUnary) assignment.getOperator(), (ConstantValue) rValue2); + } else if(assignment.getOperator() instanceof OperatorBinary && assignment.getrValue1() instanceof ConstantValue && assignment.getrValue2() instanceof ConstantValue) { + return inferTypes(symbols, (ConstantValue) rValue1, (OperatorBinary) assignment.getOperator(), (ConstantValue) rValue2); + } else { + return new ArrayList<>(); + } + } + + /** + * Infer the potential types for a binary operator with {@link SymbolType#NUMBER} type + * + * @return The potential types + */ + public static List inferTypes(ProgramScope programScope, ConstantValue leftValue, OperatorBinary operator, ConstantValue rightValue) { + if(SymbolType.NUMBER.equals(leftValue.getType(programScope)) && SymbolType.NUMBER.equals(rightValue.getType(programScope))) { + // Calculate resulting constant literal + ConstantLiteral leftLiteral = leftValue.calculateLiteral(programScope); + ConstantLiteral rightLiteral = rightValue.calculateLiteral(programScope); + ConstantLiteral constantLiteral = operator.calculateLiteral(leftLiteral, rightLiteral); + return inferTypes(programScope, constantLiteral); + } else { + throw new InternalError("Both operands must be number type."); + } + } + + /** + * Infer the potential types for a unary operator with {@link SymbolType#NUMBER} type + * + * @return The potential types + */ + public static List inferTypes(ProgramScope programScope, OperatorUnary operator, ConstantValue constantValue) { + if(SymbolType.NUMBER.equals(constantValue.getType(programScope))) { + // Calculate resulting constant literal + ConstantLiteral operandLiteral = constantValue.calculateLiteral(programScope); + ConstantLiteral constantLiteral = operator.calculateLiteral(operandLiteral, programScope); + return inferTypes(programScope, constantLiteral); + } else { + throw new InternalError("Operand must be number type."); + } + } + + /** + * Infer the potential types for a constant value with {@link SymbolType#NUMBER} type + * + * @return The potential types + */ + public static List inferTypes(ProgramScope programScope, ConstantValue constantValue) { + // Calculate resulting constant literal + ConstantLiteral constantLiteral = constantValue.calculateLiteral(programScope); + return inferTypes(programScope, constantLiteral); + } + + /** + * Infer the potential types for a constant literal with {@link SymbolType#NUMBER} type + */ + public static List inferTypes(ProgramScope programScope, ConstantLiteral literal) { + if(literal instanceof ConstantInteger && SymbolType.NUMBER.equals(literal.getType(programScope))) { + ArrayList potentialTypes = new ArrayList<>(); + ConstantInteger constantInteger = (ConstantInteger) literal; + Long number = constantInteger.getValue(); + for(SymbolTypeIntegerFixed typeInteger : SymbolType.getIntegerFixedTypes()) { + if(number >= typeInteger.getMinValue() && number <= typeInteger.getMaxValue()) { + potentialTypes.add(typeInteger); + } + } + return potentialTypes; + } else { + throw new InternalError("Literal must number type."); + } + } + +} diff --git a/src/main/java/dk/camelot64/kickc/model/values/ConstantInteger.java b/src/main/java/dk/camelot64/kickc/model/values/ConstantInteger.java index c4f8620eb..7eccd45a4 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ConstantInteger.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ConstantInteger.java @@ -1,30 +1,29 @@ package dk.camelot64.kickc.model.values; import dk.camelot64.kickc.fragment.AsmFormat; -import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeMulti; /** Constant integer value */ public class ConstantInteger implements ConstantEnumerable { private Long number; - /** The type of the number. (Either specific or the multi-type)*/ + /** The type of the number. (Either fixed size og the "number" type) */ private SymbolType type; public ConstantInteger(Long number) { this.number = number; - this.type = SymbolTypeMulti.getMultiType(number); + this.type = SymbolType.NUMBER; } public ConstantInteger(Long number, SymbolType type) { this.number = number; - if(type!=null) { + if(type != null) { this.type = type; } else { - this.type = SymbolTypeMulti.getMultiType(number); + this.type = SymbolType.NUMBER; } } @@ -45,6 +44,10 @@ public class ConstantInteger implements ConstantEnumerable { return type; } + public void setType(SymbolType type) { + this.type = type; + } + @Override public String toString() { return toString(null); @@ -71,4 +74,5 @@ public class ConstantInteger implements ConstantEnumerable { public int hashCode() { return number != null ? number.hashCode() : 0; } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index efc72cdbd..65afaf38f 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -499,7 +499,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { if(initializer != null) { addInitialAssignment(initializer, lValue, comments); } else { - if(type instanceof SymbolTypeInteger) { + if(type instanceof SymbolTypeIntegerFixed) { // Add an zero value initializer ConstantInteger zero = new ConstantInteger(0L); Statement stmt = new StatementAssignment(lValue.getRef(), zero, new StatementSource(ctx), ensureUnusedComments(comments)); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AddTypePromotions.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AddTypePromotions.java deleted file mode 100644 index afe0863aa..000000000 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1AddTypePromotions.java +++ /dev/null @@ -1,102 +0,0 @@ -package dk.camelot64.kickc.passes; - -import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.operators.Operators; -import dk.camelot64.kickc.model.statements.StatementSource; -import dk.camelot64.kickc.model.values.LValue; -import dk.camelot64.kickc.model.statements.Statement; -import dk.camelot64.kickc.model.statements.StatementAssignment; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypePointer; - -import java.util.List; -import java.util.ListIterator; - -/** - * Add casts in all assignments where types are not equal, but the rValue type can be promoted to the lValue type. - */ -public class Pass1AddTypePromotions extends Pass1Base { - - public Pass1AddTypePromotions(Program program) { - super(program); - } - - @Override - public boolean step() { - for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) { - List statements = block.getStatements(); - ListIterator stmtIt = statements.listIterator(); - while(stmtIt.hasNext()) { - Statement statement = stmtIt.next(); - if(statement instanceof StatementAssignment) { - getPromotionAssignment((StatementAssignment) statement, stmtIt); - } - // TODO: Implement promotion for calls - } - } - return false; - } - - /** - * Examines an assignment to determine if a cast of the rValue is needed (the lvalue type and the rvalue type is not equal) - * and possible (the types are promotion compatible). - *

- * If a promotion is needed it is added by adding a new tmp-var with a cast and modifying the statement. - * - * @param assignment The assignment to examine - * @param stmtIt Iterator allowing the method to add a tmp-var-assignment. - */ - private void getPromotionAssignment(StatementAssignment assignment, ListIterator stmtIt) { - LValue lValue = assignment.getlValue(); - SymbolType lValueType = SymbolTypeInference.inferType(getScope(), lValue); - SymbolType rValueType = SymbolTypeInference.inferTypeRValue(getScope(), assignment); - if(SymbolTypeInference.typeMatch(lValueType, rValueType)) { - return; - } - // No direct type match - attempt promotion - if(canPromote(lValueType, rValueType)) { - // Promotion possible - add tmp-var and a cast - if(assignment.getOperator() == null) { - // No operator - add cast directly! - assignment.setOperator(Operators.getCastUnary(lValueType)); - if(getLog().isVerbosePass1CreateSsa()) { - getLog().append("Promoting " + rValueType + " to " + lValueType + " in " + assignment); - } - } else { - throw new RuntimeException("Tmp-var promotions not implemented yet " + assignment); - } - } else { - String msg = "ERROR! Type mismatch (" + lValueType.getTypeName() + ") cannot be assigned from (" + rValueType.getTypeName() + "). " + - "In " + assignment.toString(getProgram(), false); - getProgram().getLog().append(msg); - throw new CompileError(msg, assignment.getSource()); - } - } - - /** - * Determines if it is possible to promote (cast without loss) one type to another - * - * @param lValueType The type of the lValue - * @param rValueType The type of the rValue (that will be cast) - * @return True if a cast is possible without any loss - */ - private boolean canPromote(SymbolType lValueType, SymbolType rValueType) { - if(lValueType instanceof SymbolTypePointer && SymbolType.isWord(rValueType)) { - return true; - } - if(lValueType.equals(SymbolType.WORD) && SymbolType.isByte(rValueType)) { - return true; - } - if(lValueType.equals(SymbolType.DWORD) && SymbolType.isWord(rValueType)) { - return true; - } - if(lValueType.equals(SymbolType.DWORD) && SymbolType.isByte(rValueType)) { - return true; - } - // No type promotion found - return false; - } - - -} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1Procedures.java b/src/main/java/dk/camelot64/kickc/passes/Pass1Procedures.java new file mode 100644 index 000000000..8ba6364d2 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1Procedures.java @@ -0,0 +1,43 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementCall; +import dk.camelot64.kickc.model.symbols.Procedure; +import dk.camelot64.kickc.model.symbols.Scope; + +/** + * Updates procedure calls to point to the actual procedure called. + */ +public class Pass1Procedures extends Pass2SsaOptimization { + + public Pass1Procedures(Program program) { + super(program); + } + + @Override + public boolean step() { + for(ControlFlowBlock block : getGraph().getAllBlocks()) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementCall) { + StatementCall call = (StatementCall) statement; + String procedureName = call.getProcedureName(); + Scope currentScope = getScope().getScope(block.getScope()); + Procedure procedure = currentScope.getProcedure(procedureName); + if(procedure == null) { + throw new CompileError("Called procedure not found. " + call.toString(getProgram(), false), statement.getSource()); + } + call.setProcedure(procedure.getRef()); + if(procedure.getParameters().size() != call.getParameters().size()) { + throw new CompileError("Wrong number of parameters in call. Expected " + procedure.getParameters().size() + ". " + statement.toString(), statement.getSource()); + } + } + + } + } + return false; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertTypeMatch.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertTypeMatch.java index ea2611f26..466599cf3 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertTypeMatch.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertTypeMatch.java @@ -30,7 +30,7 @@ public class Pass2AssertTypeMatch extends Pass2SsaAssertion { if(conditionalJump.getOperator()==null) { RValue rValue = conditionalJump.getrValue2(); SymbolType rValueType = SymbolTypeInference.inferType(getScope(), rValue); - if(!SymbolTypeInference.typeMatch(SymbolType.BOOLEAN, rValueType)) { + if(!SymbolType.BOOLEAN.equals(rValueType)) { getLog().append("ERROR! Type mismatch non-boolean condition from (" + rValueType.getTypeName() + "). In " + statement.toString(getProgram(), false)); throw new CompileError("ERROR! Type mismatch non-boolean condition from (" + rValueType.getTypeName() + "). In " + statement.toString(getProgram(), false), statement.getSource()); } @@ -48,10 +48,11 @@ public class Pass2AssertTypeMatch extends Pass2SsaAssertion { LValue lValue = statement.getlValue(); SymbolType lValueType = SymbolTypeInference.inferType(getScope(), lValue); SymbolType rValueType = SymbolTypeInference.inferTypeRValue(getScope(), statement); - if(SymbolTypeInference.typeMatch(lValueType, rValueType)) { + if(lValueType.equals(rValueType)) { return; } - if(SymbolTypeInference.typeMatch(rValueType, lValueType)) { + if(SymbolType.NUMBER.equals(rValueType) && SymbolType.isInteger(lValueType)) { + // L-value is still a number - constants are probably not done being identified & typed return; } // Types do not match diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ComparisonOptimization.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ComparisonOptimization.java index 3c9658e9c..0354d3104 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ComparisonOptimization.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ComparisonOptimization.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementConditionalJump; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; +import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed; import dk.camelot64.kickc.model.values.ConstantBinary; import dk.camelot64.kickc.model.values.ConstantInteger; import dk.camelot64.kickc.model.values.ConstantLiteral; @@ -48,7 +48,7 @@ public class Pass2ComparisonOptimization extends Pass2SsaOptimization { } catch(ConstantNotLiteral e) { // Ignore } - if(Operators.GT.equals(operator) && valueType instanceof SymbolTypeInteger && constantLiteral instanceof ConstantInteger) { + if(Operators.GT.equals(operator) && valueType instanceof SymbolTypeIntegerFixed && constantLiteral instanceof ConstantInteger) { // Found > C - rewrite to >= C+1 if possible Long longValue = (Long) constantLiteral.getValue(); if(longValue > 0x00L && longValue < 0xffL) { @@ -58,7 +58,7 @@ public class Pass2ComparisonOptimization extends Pass2SsaOptimization { conditionalJump.setrValue2(new ConstantBinary(constantValue, Operators.PLUS, new ConstantInteger(1L))); } } - if(Operators.LE.equals(operator) && valueType instanceof SymbolTypeInteger && constantLiteral instanceof ConstantInteger) { + if(Operators.LE.equals(operator) && valueType instanceof SymbolTypeIntegerFixed && constantLiteral instanceof ConstantInteger) { // Found <= C - rewrite to < C+1 if possible Long longValue = (Long) constantLiteral.getValue(); if(longValue > 0x00L && longValue < 0xffL) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index f3c968da0..0cde91c25 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -72,10 +72,8 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { SymbolType constType = variableType; if(!valueType.equals(variableType)) { - if(SymbolTypeInference.typeMatch(variableType, valueType)) { + if(variableType.equals(valueType)) { constType = variableType; - } else if(SymbolTypeInference.typeMatch(valueType, variableType)) { - constType = valueType; } else { throw new CompileError( "Constant variable has a non-matching type \n variable: " + variable.toString(getProgram()) + @@ -194,86 +192,12 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { // Volatile variables cannot be constant return; } - ConstantValue constant = getConstantAssignmentValue(assignment, var.getType()); - if(constant != null) { - constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); + if(assignment.getrValue1()==null && assignment.getOperator()==null && assignment.getrValue2() instanceof ConstantValue) { + constants.put(variable, new ConstantVariableValue(variable, (ConstantValue) assignment.getrValue2(), assignment)); } } } - /** - * Examine the right side of an assignment and if it is constant then return the constant value. - * - * @param assignment The assignment to examine - * @param lValueType The type of the lvalue - * @return The constant value if the right side is constant - */ - private ConstantValue getConstantAssignmentValue(StatementAssignment assignment, SymbolType lValueType) { - if(assignment.getrValue1() == null && getConstant(assignment.getrValue2()) != null) { - if(assignment.getOperator() == null) { - // Constant assignment - return getConstant(assignment.getrValue2()); - } else { - // Constant unary expression - return createUnary( - (OperatorUnary) assignment.getOperator(), - getConstant(assignment.getrValue2()) - ); - } - } else if(getConstant(assignment.getrValue1()) != null && getConstant(assignment.getrValue2()) != null) { - // Constant binary expression - return createBinary( - getConstant(assignment.getrValue1()), - (OperatorBinary) assignment.getOperator(), - getConstant(assignment.getrValue2()), - getScope()); - } else if(assignment.getrValue2() instanceof ValueList && assignment.getOperator() == null && assignment.getrValue1() == null) { - // A candidate for a constant list - examine to confirm - if(lValueType instanceof SymbolTypeArray) { - ValueList valueList = (ValueList) assignment.getrValue2(); - List values = valueList.getList(); - boolean allConstant = true; - // Type of the elements of the list (deducted from the type of all elements) - SymbolType listType = null; - List elements = new ArrayList<>(); - for(RValue elmValue : values) { - if(elmValue instanceof ConstantValue) { - ConstantValue constantValue = (ConstantValue) elmValue; - SymbolType elmType = constantValue.getType(getScope()); - if(listType == null) { - listType = elmType; - } else { - if(!SymbolTypeInference.typeMatch(listType, elmType)) { - SymbolType intersectType = SymbolTypeInference.intersectTypes(listType, elmType); - if(intersectType == null) { - // No overlap between list type and element type - throw new RuntimeException("Array type " + listType + " does not match element type" + elmType + ". Array: " + valueList.toString(getProgram())); - } else { - listType = intersectType; - } - } - } - elements.add(constantValue); - } else { - allConstant = false; - listType = null; - break; - } - } - if(allConstant && listType != null) { - // Constant list confirmed! - return new ConstantArrayList(elements, listType); - } - } - } else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1() == null) { - // Constant address-of variable - if(assignment.getrValue2() instanceof SymbolRef) { - return new ConstantSymbolPointer((SymbolRef) assignment.getrValue2()); - } - } - return null; - } - /** * If the rValue is a known constant return the constant value. * diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantRValueConsolidation.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantRValueConsolidation.java new file mode 100644 index 000000000..a473bd5f8 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantRValueConsolidation.java @@ -0,0 +1,125 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.operators.OperatorBinary; +import dk.camelot64.kickc.model.operators.OperatorUnary; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeArray; +import dk.camelot64.kickc.model.types.SymbolTypeInference; +import dk.camelot64.kickc.model.values.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * Compiler Pass consolidating L-values that are constant into a single {@link ConstantValue} + */ +public class Pass2ConstantRValueConsolidation extends Pass2SsaOptimization { + + public Pass2ConstantRValueConsolidation(Program program) { + super(program); + } + + /** + * Propagate constants, replacing variables with constants where possible. + * + * @return true optimization was performed. false if no optimization was possible. + */ + @Override + public boolean step() { + boolean modified = false; + for(ControlFlowBlock block : getGraph().getAllBlocks()) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + if(assignment.getrValue1() != null || assignment.getOperator() != null || !(assignment.getrValue2() instanceof ConstantValue)) { + SymbolType lValueType = SymbolTypeInference.inferType(getScope(), assignment.getlValue()); + ConstantValue constant = getConstantAssignmentValue(assignment, lValueType); + if(constant != null) { + getLog().append("Constant right-side identified " + assignment.toString(getProgram(), false)); + assignment.setrValue2(constant); + assignment.setOperator(null); + assignment.setrValue1(null); + modified = true; + } + } + } + } + } + return modified; + } + + /** + * Examine the right side of an assignment and if it is constant then return the constant value. + * + * @param assignment The assignment to examine + * @param lValueType The type of the lvalue + * @return The constant value if the right side is constant + */ + private ConstantValue getConstantAssignmentValue(StatementAssignment assignment, SymbolType lValueType) { + + if(assignment.getrValue1() == null && Pass2ConstantIdentification.getConstant(assignment.getrValue2()) != null) { + if(assignment.getOperator() == null) { + // Constant assignment + return Pass2ConstantIdentification.getConstant(assignment.getrValue2()); + } else { + // Constant unary expression + return Pass2ConstantIdentification.createUnary( + (OperatorUnary) assignment.getOperator(), + Pass2ConstantIdentification.getConstant(assignment.getrValue2()) + ); + } + } else if(Pass2ConstantIdentification.getConstant(assignment.getrValue1()) != null && Pass2ConstantIdentification.getConstant(assignment.getrValue2()) != null) { + // Constant binary expression + return Pass2ConstantIdentification.createBinary( + Pass2ConstantIdentification.getConstant(assignment.getrValue1()), + (OperatorBinary) assignment.getOperator(), + Pass2ConstantIdentification.getConstant(assignment.getrValue2()), + getScope()); + } else if(assignment.getrValue2() instanceof ValueList && assignment.getOperator() == null && assignment.getrValue1() == null) { + // A candidate for a constant list - examine to confirm + if(lValueType instanceof SymbolTypeArray) { + ValueList valueList = (ValueList) assignment.getrValue2(); + List values = valueList.getList(); + boolean allConstant = true; + // Type of the elements of the list (deducted from the type of all elements) + SymbolType listType = null; + List elements = new ArrayList<>(); + for(RValue elmValue : values) { + if(elmValue instanceof ConstantValue) { + ConstantValue constantValue = (ConstantValue) elmValue; + SymbolType elmType = constantValue.getType(getScope()); + if(listType == null) { + listType = elmType; + } else { + if(!listType.equals(elmType)) { + // No overlap between list type and element type + throw new RuntimeException("Array type " + listType + " does not match element type" + elmType + ". Array: " + valueList.toString(getProgram())); + } + } + elements.add(constantValue); + } else { + allConstant = false; + listType = null; + break; + } + } + if(allConstant && listType != null) { + // Constant list confirmed! + return new ConstantArrayList(elements, listType); + } + } + } else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1() == null) { + // Constant address-of variable + if(assignment.getrValue2() instanceof SymbolRef) { + return new ConstantSymbolPointer((SymbolRef) assignment.getrValue2()); + } + } + return null; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructors.java b/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructors.java index 1bdd74241..49b6afce4 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructors.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2FixInlineConstructors.java @@ -15,7 +15,7 @@ import dk.camelot64.kickc.model.symbols.VariableIntermediate; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeArray; import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; +import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed; import dk.camelot64.kickc.model.values.Value; import dk.camelot64.kickc.model.values.ValueList; @@ -68,11 +68,11 @@ public class Pass2FixInlineConstructors extends Pass2SsaOptimization { private abstract class InlineConstructor implements ProgramValueHandler { - private SymbolTypeInteger constructType; + private SymbolTypeIntegerFixed constructType; private Operator constructOperator; private boolean optimized; - public InlineConstructor(SymbolTypeInteger constructType, Operator constructOperator) { + public InlineConstructor(SymbolTypeIntegerFixed constructType, Operator constructOperator) { this.constructType = constructType; this.constructOperator = constructOperator; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2RangeResolving.java b/src/main/java/dk/camelot64/kickc/passes/Pass2RangeResolving.java index b5694442d..5bc71e830 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2RangeResolving.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2RangeResolving.java @@ -6,7 +6,7 @@ import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeInteger; +import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed; import dk.camelot64.kickc.model.types.SymbolTypePointer; import dk.camelot64.kickc.model.values.*; @@ -51,9 +51,9 @@ public class Pass2RangeResolving extends Pass2SsaOptimization { if(rangeValue instanceof RangeComparison) { SymbolType type = ((RangeComparison) rangeValue).getType(); - SymbolTypeInteger valueType; - if(type instanceof SymbolTypeInteger) { - valueType = (SymbolTypeInteger) type; + SymbolTypeIntegerFixed valueType; + if(type instanceof SymbolTypeIntegerFixed) { + valueType = (SymbolTypeIntegerFixed) type; } else if(type instanceof SymbolTypePointer) { valueType = SymbolType.WORD; } else { diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversions.java b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversions.java new file mode 100644 index 000000000..bf0f55c6f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversions.java @@ -0,0 +1,134 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.types.*; +import dk.camelot64.kickc.model.values.ConstantInteger; +import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.RValue; + +import java.util.List; +import java.util.ListIterator; + +/** + * Add casts in all assignments where types are not equal, but the rValue type can be converted to the lValue type. + */ +public class PassNAddTypeConversions extends Pass2SsaOptimization { + + public PassNAddTypeConversions(Program program) { + super(program); + } + + @Override + public boolean step() { + for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) { + List statements = block.getStatements(); + ListIterator stmtIt = statements.listIterator(); + while(stmtIt.hasNext()) { + Statement statement = stmtIt.next(); + if(statement instanceof StatementAssignment) { + doConversionAssignment((StatementAssignment) statement, stmtIt); + } + // TODO: Implement conversion for calls + } + } + return false; + } + + /** + * Examines an assignment to determine if a cast of the rValue is needed (the lvalue type and the rvalue type is not equal) + * and possible (the types are conversion compatible). + *

+ * If a conversion is needed it is added by adding a new tmp-var with a cast and modifying the statement. + * + * @param assignment The assignment to examine + * @param stmtIt Iterator allowing the method to add a tmp-var-assignment. + */ + private void doConversionAssignment(StatementAssignment assignment, ListIterator stmtIt) { + LValue lValue = assignment.getlValue(); + SymbolType lValueType = SymbolTypeInference.inferType(getScope(), lValue); + SymbolType rValueType = SymbolTypeInference.inferTypeRValue(getScope(), assignment); + if(lValueType.equals(rValueType)) { + return; + } + if(SymbolType.NUMBER.equals(rValueType)) { + // If the rValue is number type - find the potential types based on the literal value + List potentialTypes = SymbolTypeNumberInference.inferTypesRValue(getScope(), assignment); + if(potentialTypes.size()==0) { + // number type cannot be calculated currently - save for later + return; + } + for(SymbolTypeIntegerFixed potentialType : potentialTypes) { + if(lValueType.equals(potentialType) || canConvert(lValueType, potentialType)) { + addConversionCast(assignment, lValueType, rValueType); + return; + } + } + } else { + // No direct type match - attempt conversion + if(canConvert(lValueType, rValueType)) { + addConversionCast(assignment, lValueType, rValueType); + return; + } + } + + // Conversion not possible - report a type error! + String msg = "ERROR! Type mismatch (" + lValueType.getTypeName() + ") cannot be assigned from (" + rValueType.getTypeName() + "). " + + "In " + assignment.toString(getProgram(), false); + getProgram().getLog().append(msg); + throw new CompileError(msg, assignment.getSource()); + } + + /** + * Add a conversion cast to the rvalue of an assignment + * + * @param assignment The assignment to add the cast to + * @param lValueType The type of the lValue + * @param rValueType The type of the rValue + */ + private void addConversionCast(StatementAssignment assignment, SymbolType lValueType, SymbolType rValueType) { + // Promotion possible - add tmp-var and a cast + if(assignment.getOperator() == null) { + // No operator - add cast directly! + RValue rValue = assignment.getrValue2(); + if((rValue instanceof ConstantInteger) && SymbolType.NUMBER.equals(((ConstantInteger) rValue).getType()) && SymbolType.isInteger(lValueType)) { + ((ConstantInteger) rValue).setType(lValueType); + } else { + assignment.setOperator(Operators.getCastUnary(lValueType)); + } + if(getLog().isVerbosePass1CreateSsa()) { + getLog().append("Converting " + rValueType + " to " + lValueType + " in " + assignment); + } + } else { + throw new RuntimeException("Tmp-var conversion not implemented yet " + assignment); + } + } + + /** + * Determines if it is possible to convert one type to another + * as described in C99 6.3.1.8 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=70 + * + * @param lValueType The type of the lValue + * @param rValueType The type of the rValue (that will be cast) + * @return True if a cast is possible without any loss + */ + private boolean canConvert(SymbolType lValueType, SymbolType rValueType) { + if(lValueType instanceof SymbolTypePointer && SymbolType.isWord(rValueType)) { + return true; + } + if(lValueType instanceof SymbolTypeInteger && rValueType instanceof SymbolTypeInteger) { + SymbolType convertedMathType = SymbolType.convertedMathType((SymbolTypeInteger) lValueType, (SymbolTypeInteger) rValueType); + if(lValueType.equals(convertedMathType)) { + return true; + } + } + // No type promotion found + return false; + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java b/src/main/java/dk/camelot64/kickc/passes/PassNTypeInference.java similarity index 68% rename from src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java rename to src/main/java/dk/camelot64/kickc/passes/PassNTypeInference.java index 0f296642d..d4baa4e7d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNTypeInference.java @@ -12,9 +12,9 @@ import dk.camelot64.kickc.model.types.SymbolTypeInference; * Pass through the generated statements inferring types of unresolved variables. * Also updates procedure calls to point to the actual procedure called. */ -public class Pass1TypeInference extends Pass1Base { +public class PassNTypeInference extends Pass2SsaOptimization { - public Pass1TypeInference(Program program) { + public PassNTypeInference(Program program) { super(program); } @@ -38,17 +38,6 @@ public class Pass1TypeInference extends Pass1Base { } } } else if(statement instanceof StatementCall) { - StatementCall call = (StatementCall) statement; - String procedureName = call.getProcedureName(); - Scope currentScope = getScope().getScope(block.getScope()); - Procedure procedure = currentScope.getProcedure(procedureName); - if(procedure == null) { - throw new CompileError("Called procedure not found. " + call.toString(getProgram(), false), statement.getSource()); - } - call.setProcedure(procedure.getRef()); - if(procedure.getParameters().size() != call.getParameters().size()) { - throw new CompileError("Wrong number of parameters in call. Expected " + procedure.getParameters().size() + ". " + statement.toString(), statement.getSource()); - } SymbolTypeInference.inferCallLValue(getProgram(), (StatementCall) statement, false); } else if(statement instanceof StatementCallPointer) { SymbolTypeInference.inferCallPointerLValue(getProgram(), (StatementCallPointer) statement, false); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index cbc3675db..89f5ff376 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,11 +32,21 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testIntegerLiteralsMin() throws IOException, URISyntaxException { + compileAndCompare("int-literals-min"); + } + @Test public void testIntegerLiterals() throws IOException, URISyntaxException { compileAndCompare("int-literals"); } + @Test + public void testNumberType() throws IOException, URISyntaxException { + compileAndCompare("number-type", log()); + } + @Test public void testSimpleLoop() throws IOException, URISyntaxException { compileAndCompare("simple-loop"); @@ -1636,7 +1646,7 @@ public class TestPrograms { @Test public void testForClassicMin() throws IOException, URISyntaxException { - compileAndCompare("forclassicmin"); + compileAndCompare("forclassicmin", log()); } @Test diff --git a/src/test/kc/int-literals.kc b/src/test/kc/int-literals.kc index d3fd609c6..5afa560bb 100644 --- a/src/test/kc/int-literals.kc +++ b/src/test/kc/int-literals.kc @@ -1,59 +1,106 @@ // Tests different integer literal types -const byte RED = 2; -const byte GREEN = 5; +const byte RED = 2ub; +const byte GREEN = 5ub; -const byte* SCREEN = $0400; -const byte* COLS = $d800; -byte idx = 0; +const byte* SCREEN = $0400uw; +const byte* COLS = $d800uw; +byte idx = 0ub; void main() { - for(byte* s: SCREEN..SCREEN+999) *s = ' '; - idx = 0; + for(byte* s=SCREEN;s>1; + SCREENB[idx++] = 15&28; + SCREENB[idx++] = 4|8; + SCREENB[idx++] = 5^9; + SCREENB[idx++] = (2+2)*(15/5); + SCREENB[idx++] = (byte)(4096+12); +} + +