mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-25 18:29:15 +00:00
Implemented much of integer type conversion and the number type. Still a lot left to do.
This commit is contained in:
parent
facd8c6c5b
commit
35ec65ce94
@ -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));
|
||||
|
@ -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"
|
||||
*/
|
||||
|
24
src/main/java/dk/camelot64/kickc/model/InternalError.java
Normal file
24
src/main/java/dk/camelot64/kickc/model/InternalError.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
43
src/main/java/dk/camelot64/kickc/passes/Pass1Procedures.java
Normal file
43
src/main/java/dk/camelot64/kickc/passes/Pass1Procedures.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
@ -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
|
||||
|
@ -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;
|
||||
|
23
src/test/kc/number-type.kc
Normal file
23
src/test/kc/number-type.kc
Normal 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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user