1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-26 09:29:18 +00:00

Implemented much of integer type conversion and the number type. Still a lot left to do.

This commit is contained in:
jespergravgaard 2019-05-05 01:26:40 +02:00
parent facd8c6c5b
commit 35ec65ce94
39 changed files with 793 additions and 538 deletions

View File

@ -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<Pass2SsaOptimization> 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));

View File

@ -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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> 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"
*/

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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);

View File

@ -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<SymbolTypeInteger> getIntegerTypes() {
ArrayList<SymbolTypeInteger> types = new ArrayList<>();
static Collection<SymbolTypeIntegerFixed> getIntegerFixedTypes() {
ArrayList<SymbolTypeIntegerFixed> 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;
}

View File

@ -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<SymbolType> 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<SymbolType> 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<SymbolType> rTypes = ((SymbolTypeMulti) rValueType).getTypes();
if(lValueType instanceof SymbolTypeMulti) {
// Both are inline types - RValue type must be superset of LValue
Collection<SymbolType> 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<SymbolType> lTypes, Collection<SymbolType> 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<SymbolType> 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) {

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -34,7 +34,7 @@ public class SymbolTypeMulti implements SymbolType {
*/
public static SymbolType getMultiType(Long number) {
ArrayList<SymbolType> potentialTypes = new ArrayList<>();
for(SymbolTypeInteger typeInteger : SymbolType.getIntegerTypes()) {
for(SymbolTypeIntegerFixed typeInteger : SymbolType.getIntegerFixedTypes()) {
if(number >= typeInteger.getMinValue() && number <= typeInteger.getMaxValue()) {
potentialTypes.add(typeInteger);
}

View File

@ -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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> 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<SymbolTypeIntegerFixed> inferTypes(ProgramScope programScope, ConstantLiteral literal) {
if(literal instanceof ConstantInteger && SymbolType.NUMBER.equals(literal.getType(programScope))) {
ArrayList<SymbolTypeIntegerFixed> 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.");
}
}
}

View File

@ -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<Long> {
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<Long> {
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<Long> {
public int hashCode() {
return number != null ? number.hashCode() : 0;
}
}

View File

@ -499,7 +499,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
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));

View File

@ -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<Statement> statements = block.getStatements();
ListIterator<Statement> 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).
* <p>
* 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<Statement> 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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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<RValue> values = valueList.getList();
boolean allConstant = true;
// Type of the elements of the list (deducted from the type of all elements)
SymbolType listType = null;
List<ConstantValue> 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.
*

View File

@ -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<RValue> values = valueList.getList();
boolean allConstant = true;
// Type of the elements of the list (deducted from the type of all elements)
SymbolType listType = null;
List<ConstantValue> 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;
}
}

View File

@ -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;
}

View File

@ -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 {

View File

@ -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<Statement> statements = block.getStatements();
ListIterator<Statement> 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).
* <p>
* 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<Statement> 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<SymbolTypeIntegerFixed> 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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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<SCREEN+1000uw;s++) *s = ' ';
testSimpleTypes();
idx = 40;
testUnaryOperator();
testBinaryOperator();
}
void testSimpleTypes() {
idx = 0ub;
// Simple types
assertType(typeid(12ub), typeid(byte));
assertType(typeid(12uc), typeid(byte));
assertType(typeid(12ub), typeid(unsigned byte));
assertType(typeid(12uc), typeid(unsigned byte));
assertType(typeid(12sb), typeid(signed byte));
assertType(typeid(12sc), typeid(signed byte));
assertType(typeid(12uw), typeid(word));
assertType(typeid(12ui), typeid(word));
assertType(typeid(12us), typeid(word));
assertType(typeid(12uw), typeid(unsigned word));
assertType(typeid(12ui), typeid(unsigned word));
assertType(typeid(12us), typeid(unsigned word));
assertType(typeid(12sw), typeid(signed word));
assertType(typeid(12si), typeid(signed word));
assertType(typeid(12ss), typeid(signed word));
assertType(typeid(12ud), typeid(dword));
assertType(typeid(12ul), typeid(dword));
assertType(typeid(12ud), typeid(unsigned dword));
assertType(typeid(12ul), typeid(unsigned dword));
assertType(typeid(12sd), typeid(signed dword));
assertType(typeid(12sl), typeid(signed dword));
assertType(typeid(12l), typeid(signed dword));
}
void testUnaryOperator() {
idx = 40ub;
// Unary Operations
assertType(typeid(-12ub), typeid(unsigned byte));
assertType(typeid(-12sb), typeid(signed byte));
assertType(typeid(-12uw), typeid(unsigned word));
assertType(typeid(-12sw), typeid(signed word));
assertType(typeid(-12ud), typeid(unsigned dword));
assertType(typeid(-12sd), typeid(signed dword));
}
void testBinaryOperator() {
idx = 80ub;
// Binary Operations between unsigned byte & other types
assertType(typeid(12ub+12ub), typeid(byte));
assertType(typeid(12ub+12sb), typeid(byte));
assertType(typeid(12ub+12uw), typeid(word));
assertType(typeid(12ub+12ub), typeid(unsigned byte));
assertType(typeid(12ub+12sb), typeid(unsigned byte));
assertType(typeid(12ub+12uw), typeid(unsigned word));
assertType(typeid(12ub+12sw), typeid(signed word));
assertType(typeid(12ub+12ud), typeid(dword));
assertType(typeid(12ub+12ud), typeid(unsigned dword));
assertType(typeid(12ub+12sd), typeid(signed dword));
idx++;
// Binary Operations between signed byte & other types
assertType(typeid(12sb+12ub), typeid(byte));
assertType(typeid(12sb+12ub), typeid(unsigned byte));
assertType(typeid(12sb+12sb), typeid(signed byte));
assertType(typeid(12sb+12uw), typeid(word));
assertType(typeid(12sb+12uw), typeid(unsigned word));
assertType(typeid(12sb+12sw), typeid(signed word));
assertType(typeid(12sb+12ud), typeid(dword));
assertType(typeid(12sb+12ud), typeid(unsigned dword));
assertType(typeid(12sb+12sd), typeid(signed dword));
idx++;
// Binary Operations between unsigned word & other types
assertType(typeid(12uw+12ub), typeid(unsigned word));
assertType(typeid(12uw+12sb), typeid(unsigned word));
assertType(typeid(12uw+12uw), typeid(unsigned word));
assertType(typeid(12uw+12sw), typeid(unsigned word));
assertType(typeid(12uw+12ud), typeid(unsigned dword));
assertType(typeid(12uw+12sd), typeid(signed dword));
idx = 120ub;
// Binary Operations between signed word & other types
assertType(typeid(12sw+12ub), typeid(signed word));
assertType(typeid(12sw+12sb), typeid(signed word));
assertType(typeid(12sw+12uw), typeid(unsigned word));
assertType(typeid(12sw+12sw), typeid(signed word));
assertType(typeid(12sw+12ud), typeid(unsigned dword));
assertType(typeid(12sw+12sd), typeid(signed dword));
idx++;
// Binary Operations between unsigned dword & other types
assertType(typeid(12ud+12ub), typeid(unsigned dword));
assertType(typeid(12ud+12sb), typeid(unsigned dword));
assertType(typeid(12ud+12uw), typeid(unsigned dword));
assertType(typeid(12ud+12sw), typeid(unsigned dword));
assertType(typeid(12ud+12ud), typeid(unsigned dword));
assertType(typeid(12ud+12sd), typeid(unsigned dword));
idx++;
// Binary Operations between signed dword & other types
assertType(typeid(12sd+12ub), typeid(signed dword));
assertType(typeid(12sd+12sb), typeid(signed dword));
assertType(typeid(12sd+12uw), typeid(signed dword));
assertType(typeid(12sd+12sw), typeid(signed dword));
assertType(typeid(12sd+12ud), typeid(unsigned dword));
assertType(typeid(12sd+12sd), typeid(signed dword));
}
// Check that the two passed type IDs are equal.
// Shows a letter symbolizing t1
// If they are equal the letter is green - if not it is red.
void assertType(byte t1, byte t2) {
if(t1==t2) {
COLS[idx] = GREEN;

View File

@ -0,0 +1,23 @@
// Tests the number type used for constant expressions
const byte* SCREENB = 0x0400;
void main() {
// Constant values resolvable to bytes
byte idx = 0;
SCREENB[idx++] = 12;
SCREENB[idx++] = 6+6;
SCREENB[idx++] = 18-6;
SCREENB[idx++] = 1812-1800;
SCREENB[idx++] = 1+2+3+6;
SCREENB[idx++] = 2*6;
SCREENB[idx++] = 3<<2;
SCREENB[idx++] = 24>>1;
SCREENB[idx++] = 15&28;
SCREENB[idx++] = 4|8;
SCREENB[idx++] = 5^9;
SCREENB[idx++] = (2+2)*(15/5);
SCREENB[idx++] = (byte)(4096+12);
}