1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-17 10:30:43 +00:00

Implemented framework for number type conversion - the BinaryExpressionIterator. Added first case (type match) in new PassNAddNumberTybeConversions.

This commit is contained in:
jespergravgaard 2019-05-06 01:08:41 +02:00
parent 35ec65ce94
commit d0d5b5715b
21 changed files with 738 additions and 111 deletions

View File

@ -192,7 +192,7 @@ public class CompileLog {
return verboseSSAOptimize;
}
public CompileLog setVerboseSSAOptimize() {
public CompileLog verboseSSAOptimize() {
setVerboseSSAOptimize(true);
return this;
}

View File

@ -233,6 +233,7 @@ public class Compiler {
List<Pass2SsaOptimization> optimizations = new ArrayList<>();
optimizations.add(new PassNTypeInference(program));
optimizations.add(new PassNAddTypeConversions(program));
optimizations.add(new PassNAddNumberTypeConversions(program));
optimizations.add(new Pass2CullEmptyBlocks(program));
optimizations.add(new PassNStatementIndices(program));
optimizations.add(new PassNVariableReferenceInfos(program));
@ -247,7 +248,7 @@ public class Compiler {
optimizations.add(new Pass2ConstantIdentification(program));
optimizations.add(new PassNStatementIndices(program));
optimizations.add(new PassNVariableReferenceInfos(program));
optimizations.add(new Pass2ConstantAdditionElimination(program));
//optimizations.add(new Pass2ConstantAdditionElimination(program));
optimizations.add(new Pass2ConstantIfs(program));
optimizations.add(new Pass2ConstantStringConsolidation(program));
optimizations.add(new Pass2FixInlineConstructors(program));
@ -356,7 +357,9 @@ public class Compiler {
}
private void pass3Analysis() {
new Pass3AssertNoTypeId(program).check();
new Pass3AssertRValues(program).check();
new Pass3AssertNoNumbers(program).check();
new Pass3AssertConstants(program).check();
new Pass3AssertArrayLengths(program).check();
new Pass3AssertNoMulDivMod(program).check();

View File

@ -0,0 +1,166 @@
package dk.camelot64.kickc.model.iterator;
import dk.camelot64.kickc.model.operators.OperatorBinary;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.*;
/**
* A binary expression in the program being iterated by {@link BinaryExpressionIterator}
*/
public interface BinaryExpression {
/**
* Get the left operand
*
* @return The left operand
*/
RValue getLeft();
/**
* Get the binary operator
*
* @return the operator
*/
OperatorBinary getOperator();
/**
* Get the right operand
*
* @return The right operand
*/
RValue getRight();
/**
* Adds a cast to the right operand
* @param toType The toType to cast to
*/
void addRightCast(SymbolType toType);
/** Binary expression assignment rvalue. */
class BinaryExpressionAssignmentRValue implements BinaryExpression {
private final StatementAssignment assignment;
BinaryExpressionAssignmentRValue(StatementAssignment assignment) {
this.assignment = assignment;
}
@Override
public RValue getLeft() {
return assignment.getrValue1();
}
@Override
public OperatorBinary getOperator() {
return (OperatorBinary) assignment.getOperator();
}
@Override
public RValue getRight() {
return assignment.getrValue2();
}
@Override
public void addRightCast(SymbolType toType) {
if(assignment.getrValue2() instanceof ConstantValue) {
assignment.setrValue2(new ConstantCastValue(toType, (ConstantValue) assignment.getrValue2()));
} else {
throw new InternalError("Not implemented!");
}
}
}
/** Binary expression - assignment lvalue and the "total" rvalue. */
class BinaryExpressionAssignmentLValue implements BinaryExpression {
private final StatementAssignment assignment;
BinaryExpressionAssignmentLValue(StatementAssignment assignment) {
this.assignment = assignment;
}
@Override
public RValue getLeft() {
return assignment.getlValue();
}
@Override
public OperatorBinary getOperator() {
return Operators.ASSIGNMENT;
}
@Override
public RValue getRight() {
return new AssignmentRValue(assignment);
}
@Override
public void addRightCast(SymbolType toType) {
throw new InternalError("Not implemented!");
}
}
/** Binary expression conditional jump. */
class BinaryExpressionConditionalJump implements BinaryExpression {
private final StatementConditionalJump conditionalJump;
BinaryExpressionConditionalJump(StatementConditionalJump assignment) {
this.conditionalJump = assignment;
}
@Override
public RValue getLeft() {
return conditionalJump.getrValue1();
}
@Override
public OperatorBinary getOperator() {
return (OperatorBinary) conditionalJump.getOperator();
}
@Override
public RValue getRight() {
return conditionalJump.getrValue2();
}
@Override
public void addRightCast(SymbolType toType) {
throw new InternalError("Not implemented!");
}
}
/** Binary expression as part of a constant expression. */
class BinaryExpressionConstant implements BinaryExpression {
private ConstantBinary constantBinary;
BinaryExpressionConstant(ConstantBinary constantBinary) {
this.constantBinary = constantBinary;
}
@Override
public RValue getLeft() {
return constantBinary.getLeft();
}
@Override
public OperatorBinary getOperator() {
return constantBinary.getOperator();
}
@Override
public RValue getRight() {
return constantBinary.getRight();
}
@Override
public void addRightCast(SymbolType toType) {
constantBinary.setRight(new ConstantCastValue(toType, constantBinary.getRight()));
}
}
}

View File

@ -0,0 +1,25 @@
package dk.camelot64.kickc.model.iterator;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.statements.Statement;
import java.util.ListIterator;
/** A handler that performs some action for binary expressions in the program.
* A {@link BinaryExpressionIterator} can be used to iterate all binary expressions in a part of the program.
* The Handler then receives all binary expressions one at a time. The Handler has the option of modifying the binary expression. After the handler is executed all sub-values are recursed.
* The execute() method furthermore receives some extra parameters with information about the context of the passed value.
*/
public interface BinaryExpressionHandler {
/**
* Handle a single binary expression
*
* @param binaryExpression The binary expression
* @param currentStmt The statement iterator - just past the current statement that the value is a part of. Current statment can be retrieved by calling
* @param stmtIt The statement iterator - just past the current statement. Can be used for modifying the control flow block.
* @param currentBlock The current block that the value is a part of
*/
void execute(BinaryExpression binaryExpression, Statement currentStmt, ListIterator<Statement> stmtIt, ControlFlowBlock currentBlock);
}

View File

@ -0,0 +1,63 @@
package dk.camelot64.kickc.model.iterator;
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.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.values.ConstantBinary;
import java.util.ListIterator;
/**
* Capable of iterating the different structures of a Program (graph, block, statement, symboltable, symbol).
* Creates appropriate BinaryExpressions and passes them to a BinaryExpressionHandler.
* Iteration might be guided (eg. filtering some types of the structure to iterate at call-time)
*/
public class BinaryExpressionIterator {
/**
* Execute a handler on all values in the entire program (both in the control flow graph and the symbol table.)
*
* @param program The program
* @param handler The handler to execute
*/
public static void execute(Program program, BinaryExpressionHandler handler) {
// Iterate all symbols
ProgramValueIterator.execute(program.getScope(), (programValue, currentStmt, stmtIt, currentBlock) -> {
if(programValue.get() instanceof ConstantBinary) {
ConstantBinary constantBinary = (ConstantBinary) programValue.get();
handler.execute( new BinaryExpression.BinaryExpressionConstant(constantBinary), null, null, null);
}
});
// Iterate all blocks/statements
for(ControlFlowBlock block : program.getGraph().getAllBlocks()) {
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
while(stmtIt.hasNext()) {
Statement stmt = stmtIt.next();
if(stmt instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) stmt;
if(assignment.getrValue1() != null && assignment.getOperator()!=null &&assignment.getrValue2() != null) {
handler.execute( new BinaryExpression.BinaryExpressionAssignmentRValue(assignment), stmt, stmtIt, block);
}
handler.execute( new BinaryExpression.BinaryExpressionAssignmentLValue(assignment), stmt, stmtIt, block);
} else if(stmt instanceof StatementConditionalJump) {
StatementConditionalJump condJump = (StatementConditionalJump) stmt;
if(condJump.getrValue1()!=null && condJump.getOperator()!=null && condJump.getrValue2()!=null) {
handler.execute( new BinaryExpression.BinaryExpressionConditionalJump(condJump), stmt, stmtIt, block);
}
}
// Iterate all statement values
ProgramValueIterator.execute(stmt, (programValue, currentStmt, stmtIt1, currentBlock) -> {
if(programValue.get() instanceof ConstantBinary) {
ConstantBinary constantBinary = (ConstantBinary) programValue.get();
handler.execute( new BinaryExpression.BinaryExpressionConstant(constantBinary), stmt, stmtIt, block);
}
}, stmtIt, block);
}
}
}
}

View File

@ -0,0 +1,27 @@
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.SymbolTypeSimple;
import dk.camelot64.kickc.model.values.ConstantBool;
import dk.camelot64.kickc.model.values.ConstantLiteral;
/** Binary assignment operator ( x = y ) */
public class OperatorAssignment extends OperatorBinary {
public OperatorAssignment(int precedence) {
super("=", "_assign_", precedence);
}
@Override
public ConstantLiteral calculateLiteral(ConstantLiteral left, ConstantLiteral right) {
throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right);
}
@Override
public SymbolType inferType(SymbolTypeSimple left, SymbolTypeSimple right) {
return left;
}
}

View File

@ -48,6 +48,7 @@ public class Operators {
public static final OperatorBinary BOOL_OR = new OperatorBitwiseOr(11);
public static final OperatorBinary LOGIC_AND = new OperatorLogicAnd(12);
public static final OperatorBinary LOGIC_OR = new OperatorLogicOr(13);
public static final OperatorBinary ASSIGNMENT = new OperatorAssignment(14);
public static Operator getBinary(String op) {
switch(op) {

View File

@ -20,7 +20,7 @@ public interface SymbolType {
SymbolTypeIntegerFixed DWORD = new SymbolTypeIntegerFixed("dword", 0, 4_294_967_296L, false, 32);
/** Signed double word (4 bytes, 32 bits). */
SymbolTypeIntegerFixed SDWORD = new SymbolTypeIntegerFixed("signed dword", -2_147_483_648, 2_147_483_647, true, 32);
/** Integer with unknown size. */
/** Integer with unknown size (used for constant expressions). */
SymbolTypeIntegerAuto NUMBER = new SymbolTypeIntegerAuto("number");
/** String value (treated like byte* ). */
SymbolTypeNamed STRING = new SymbolTypeNamed("string", 99);

View File

@ -126,6 +126,8 @@ public class SymbolTypeInference {
} else if(rValue instanceof ProcedureRef) {
Procedure procedure = symbols.getProcedure((ProcedureRef) rValue);
return procedure.getType();
} else if(rValue instanceof AssignmentRValue) {
return inferTypeRValue(symbols, ((AssignmentRValue) rValue).getAssignment());
}
if(type == null) {
throw new RuntimeException("Cannot infer type for " + rValue.toString());

View File

@ -0,0 +1,28 @@
package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.StatementAssignment;
/** The "total" RValue of an assigment. */
public class AssignmentRValue implements RValue {
private StatementAssignment assignment;
public AssignmentRValue(StatementAssignment assignment) {
this.assignment = assignment;
}
public StatementAssignment getAssignment() {
return assignment;
}
@Override
public String toString(Program program) {
return
(assignment.getrValue1() == null ? "" : assignment.getrValue1().toString(program) + " ") +
(assignment.getOperator() == null ? "" : assignment.getOperator() + " ") +
assignment.getrValue2().toString(program)
;
}
}

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.operators.OperatorTypeId;
import dk.camelot64.kickc.model.operators.Operators;
@ -8,9 +9,15 @@ 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.SymbolTypeIntegerFixed;
import dk.camelot64.kickc.model.types.SymbolTypeNumberInference;
import dk.camelot64.kickc.model.values.ConstantLiteral;
import dk.camelot64.kickc.model.values.ConstantRef;
import dk.camelot64.kickc.model.values.ConstantValue;
import dk.camelot64.kickc.model.values.RValue;
import java.util.List;
/**
* Converts typeid() operators to constants
*/
@ -30,11 +37,18 @@ public class Pass1TypeIdSimplification extends Pass1Base {
if(Operators.TYPEID.equals(assignment.getOperator())) {
RValue rValue = assignment.getrValue2();
SymbolType symbolType = SymbolTypeInference.inferType(getScope(), rValue);
getLog().append("Resolving typeid() " + assignment.toString(getProgram(), false));
ConstantRef typeIDConstantVar = OperatorTypeId.getTypeIdConstantVar(getScope(), symbolType);
assignment.setrValue2(typeIDConstantVar);
assignment.setOperator(null);
modified = true;
if(SymbolType.NUMBER.equals(symbolType)) {
if(rValue instanceof ConstantValue) {
List<SymbolTypeIntegerFixed> fixedTypes = SymbolTypeNumberInference.inferTypes(getScope(), (ConstantLiteral) rValue);
throw new InternalError("TODO: Implement typeof(const)!");
}
} else {
getLog().append("Resolving typeid() " + assignment.toString(getProgram(), false));
ConstantRef typeIDConstantVar = OperatorTypeId.getTypeIdConstantVar(getScope(), symbolType);
assignment.setrValue2(typeIDConstantVar);
assignment.setOperator(null);
modified = true;
}
}
}
}

View File

@ -0,0 +1,63 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.RValue;
/**
* Assert that all values with type number have been resolved to fixed size in pass 2.
*/
public class Pass3AssertNoNumbers extends Pass2SsaAssertion {
public Pass3AssertNoNumbers(Program program) {
super(program);
}
@Override
public void check() throws AssertionFailed {
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementAssignment) {
checkValue(((StatementAssignment) statement).getlValue(), statement);
checkValue(((StatementAssignment) statement).getrValue1(), statement);
checkValue(((StatementAssignment) statement).getrValue2(), statement);
} else if(statement instanceof StatementPhiBlock) {
for(StatementPhiBlock.PhiVariable phiVariable : ((StatementPhiBlock) statement).getPhiVariables()) {
for(StatementPhiBlock.PhiRValue value : phiVariable.getValues()) {
checkValue(value.getrValue(), statement);
}
}
} else if(statement instanceof StatementConditionalJump) {
checkValue(((StatementConditionalJump) statement).getrValue1(), statement);
checkValue(((StatementConditionalJump) statement).getrValue2(), statement);
}
}
}
}
/**
* Check a value to ensure it is does not have the {@link SymbolType#NUMBER}.
*
* @param rValue The value to check
* @param statement The statement containing the value
*/
public void checkValue(RValue rValue, Statement statement) {
if(rValue == null) return;
SymbolType symbolType = SymbolTypeInference.inferType(getScope(), rValue);
if(SymbolType.NUMBER.equals(symbolType)) {
throw new InternalError(
"Error! Number integer type not resolved to fixed size integer type " +
"\n value: " + rValue.toString(getProgram()) +
"\n statement: " + statement.toString(getProgram(), false)
, statement.getSource()
);
}
}
}

View File

@ -0,0 +1,34 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.values.ConstantUnary;
import dk.camelot64.kickc.model.values.Value;
/**
* Assert that no typeid() expressions exist in the code anymore (they must have been resolved to constants in phase 1-2)
*/
public class Pass3AssertNoTypeId extends Pass2SsaAssertion {
public Pass3AssertNoTypeId(Program program) {
super(program);
}
@Override
public void check() throws AssertionFailed {
ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> {
Value value = programValue.get();
if(value instanceof ConstantUnary) {
if(Operators.TYPEID.equals(((ConstantUnary) value).getOperator())) {
throw new InternalError(
"Error! Typeid() not resolved during compile. " +
"\n statement: " + currentStmt.toString(getProgram(), false)
, currentStmt.getSource()
);
}
}
});
}
}

View File

@ -1,6 +1,6 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.values.RangeValue;
@ -24,7 +24,7 @@ public class Pass3AssertRValues extends Pass2SsaAssertion {
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
Value value = programValue.get();
if(value instanceof ValueList) {
throw new CompileError(
throw new InternalError(
"Error! Value list not resolved to word constructor or array initializer" +
"\n value list: " + value.toString(getProgram()) +
"\n statement: " + currentStmt.toString(getProgram(), false)
@ -32,7 +32,7 @@ public class Pass3AssertRValues extends Pass2SsaAssertion {
);
}
if(value instanceof RangeValue) {
throw new CompileError(
throw new InternalError(
"Error! Ranged for() not resolved to constants" +
"\n Range: " + value.toString(getProgram()) +
"\n statement: " + currentStmt.toString(getProgram(), false)

View File

@ -0,0 +1,59 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.BinaryExpressionIterator;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed;
import dk.camelot64.kickc.model.values.*;
/**
* Add casts to {@link SymbolType#NUMBER} expressions that meet a typed value in a binary expression (including assignment)
*/
public class PassNAddNumberTypeConversions extends Pass2SsaOptimization {
public PassNAddNumberTypeConversions(Program program) {
super(program);
}
@Override
public boolean step() {
BinaryExpressionIterator.execute(getProgram(), (binaryExpression, currentStmt, stmtIt, currentBlock) -> {
RValue left = binaryExpression.getLeft();
RValue right = binaryExpression.getRight();
SymbolType leftType = SymbolTypeInference.inferType(getScope(), left);
SymbolType rightType = SymbolTypeInference.inferType(getScope(), right);
if(SymbolType.isInteger(leftType) && !SymbolType.NUMBER.equals(leftType) && SymbolType.NUMBER.equals(rightType)) {
SymbolTypeIntegerFixed leftIntType = (SymbolTypeIntegerFixed) leftType;
// Attempt to find number conversion!
if(right instanceof ConstantValue) {
ConstantLiteral constantLiteral = ((ConstantValue) right).calculateLiteral(getProgram().getScope());
if(constantLiteral instanceof ConstantInteger) {
ConstantInteger constantInteger = (ConstantInteger) constantLiteral;
if(SymbolType.NUMBER.equals(constantInteger.getType())) {
Long value = constantInteger.getValue();
if(leftIntType.getMinValue()<=value && leftIntType.getMaxValue()>=value) {
// The value matches the left type!
getLog().append("Adding number conversion cast ("+leftIntType+") "+binaryExpression.getRight().toString()+" in "+currentStmt.toString(getProgram(), false));
binaryExpression.addRightCast(leftIntType);
} else {
throw new InternalError("TODO: Implement number conversion for non-equal types "+right.toString(), currentStmt);
}
} else {
throw new InternalError("Non-number constant has type number "+right.toString(), currentStmt);
}
}
} else {
// Postpone til later!
// Maybe handle AssignmentRValue separately!
getLog().append("Postponed number conversion " + right.toString());
}
}
});
return false;
}
}

View File

@ -1,15 +1,19 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Comment;
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.symbols.Scope;
import dk.camelot64.kickc.model.symbols.VariableIntermediate;
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 dk.camelot64.kickc.model.values.ScopeRef;
import java.util.List;
import java.util.ListIterator;
@ -31,7 +35,7 @@ public class PassNAddTypeConversions extends Pass2SsaOptimization {
while(stmtIt.hasNext()) {
Statement statement = stmtIt.next();
if(statement instanceof StatementAssignment) {
doConversionAssignment((StatementAssignment) statement, stmtIt);
doConversionAssignment((StatementAssignment) statement, stmtIt, block);
}
// TODO: Implement conversion for calls
}
@ -48,7 +52,7 @@ public class PassNAddTypeConversions extends Pass2SsaOptimization {
* @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) {
private void doConversionAssignment(StatementAssignment assignment, ListIterator<Statement> stmtIt, ControlFlowBlock block) {
LValue lValue = assignment.getlValue();
SymbolType lValueType = SymbolTypeInference.inferType(getScope(), lValue);
SymbolType rValueType = SymbolTypeInference.inferTypeRValue(getScope(), assignment);
@ -64,14 +68,14 @@ public class PassNAddTypeConversions extends Pass2SsaOptimization {
}
for(SymbolTypeIntegerFixed potentialType : potentialTypes) {
if(lValueType.equals(potentialType) || canConvert(lValueType, potentialType)) {
addConversionCast(assignment, lValueType, rValueType);
addConversionCast(assignment, stmtIt, block, lValueType, rValueType);
return;
}
}
} else {
// No direct type match - attempt conversion
if(canConvert(lValueType, rValueType)) {
addConversionCast(assignment, lValueType, rValueType);
addConversionCast(assignment, stmtIt, block, lValueType, rValueType);
return;
}
}
@ -90,7 +94,7 @@ public class PassNAddTypeConversions extends Pass2SsaOptimization {
* @param lValueType The type of the lValue
* @param rValueType The type of the rValue
*/
private void addConversionCast(StatementAssignment assignment, SymbolType lValueType, SymbolType rValueType) {
private void addConversionCast(StatementAssignment assignment, ListIterator<Statement> stmtIt, ControlFlowBlock currentBlock, SymbolType lValueType, SymbolType rValueType) {
// Promotion possible - add tmp-var and a cast
if(assignment.getOperator() == null) {
// No operator - add cast directly!
@ -100,11 +104,16 @@ public class PassNAddTypeConversions extends Pass2SsaOptimization {
} else {
assignment.setOperator(Operators.getCastUnary(lValueType));
}
if(getLog().isVerbosePass1CreateSsa()) {
getLog().append("Converting " + rValueType + " to " + lValueType + " in " + assignment);
}
getLog().append("Converting " + rValueType + " to " + lValueType + " in " + assignment);
} else {
throw new RuntimeException("Tmp-var conversion not implemented yet " + assignment);
ScopeRef currentScope = currentBlock.getScope();
Scope blockScope = getScope().getScope(currentScope);
VariableIntermediate tmpVar = blockScope.addVariableIntermediate();
tmpVar.setType(rValueType);
StatementAssignment newAssignment = new StatementAssignment(assignment.getlValue(), Operators.getCastUnary(lValueType), tmpVar.getRef(), assignment.getSource(), Comment.NO_COMMENTS);
getLog().append("Converting " + rValueType + " to " + lValueType + " in " + assignment + " adding "+tmpVar);
assignment.setlValue(tmpVar.getRef());
stmtIt.add(newAssignment);
}
}

View File

@ -33,8 +33,18 @@ public class TestPrograms {
}
@Test
public void testIntegerLiteralsMin() throws IOException, URISyntaxException {
compileAndCompare("int-literals-min");
public void testNumberConversion() throws IOException, URISyntaxException {
compileAndCompare("number-conversion", log());
}
@Test
public void testNumberType() throws IOException, URISyntaxException {
compileAndCompare("number-type");
}
@Test
public void testIntegerConversion() throws IOException, URISyntaxException {
compileAndCompare("int-conversion");
}
@Test
@ -42,11 +52,6 @@ public class TestPrograms {
compileAndCompare("int-literals");
}
@Test
public void testNumberType() throws IOException, URISyntaxException {
compileAndCompare("number-type", log());
}
@Test
public void testSimpleLoop() throws IOException, URISyntaxException {
compileAndCompare("simple-loop");
@ -59,7 +64,7 @@ public class TestPrograms {
@Test
public void testPaulNelsenSandboxTernaryError() throws IOException, URISyntaxException {
compileAndCompare("sandbox-ternary-error", log().verboseParse().verboseCreateSsa().setVerboseSSAOptimize().verboseStatementSequence());
compileAndCompare("sandbox-ternary-error");
}
@Test
@ -75,7 +80,7 @@ public class TestPrograms {
@Test
public void testTypeIdPlusByteProblem() throws IOException, URISyntaxException {
compileAndCompare("typeid-plus-byte-problem", log());
compileAndCompare("typeid-plus-byte-problem");
}
@Test

View File

@ -0,0 +1,90 @@
// Tests different integer literal types
const byte RED = 2ub;
const byte GREEN = 5ub;
const byte* SCREEN = $0400uw;
const byte* COLS = $d800uw;
byte idx = 0ub;
void main() {
for(byte* s=SCREEN;s<SCREEN+1000uw;s++) *s = ' ';
testUnaryOperator();
testBinaryOperator();
}
void testUnaryOperator() {
idx = 00ub;
// 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 = 40ub;
// Binary Operations between unsigned byte & other types
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(unsigned dword));
assertType(typeid(12ub+12sd), typeid(signed dword));
idx++;
// Binary Operations between signed byte & other types
assertType(typeid(12sb+12ub), typeid(unsigned byte));
assertType(typeid(12sb+12sb), typeid(signed byte));
assertType(typeid(12sb+12uw), typeid(unsigned word));
assertType(typeid(12sb+12sw), typeid(signed word));
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 = 80ub;
// 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;
} else {
COLS[idx] = RED;
}
SCREEN[idx++] = t1;
}

View File

@ -10,8 +10,6 @@ byte idx = 0ub;
void main() {
for(byte* s=SCREEN;s<SCREEN+1000uw;s++) *s = ' ';
testSimpleTypes();
testUnaryOperator();
testBinaryOperator();
}
void testSimpleTypes() {
@ -34,70 +32,6 @@ void testSimpleTypes() {
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(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(unsigned dword));
assertType(typeid(12ub+12sd), typeid(signed dword));
idx++;
// Binary Operations between signed byte & other types
assertType(typeid(12sb+12ub), typeid(unsigned byte));
assertType(typeid(12sb+12sb), typeid(signed byte));
assertType(typeid(12sb+12uw), typeid(unsigned word));
assertType(typeid(12sb+12sw), typeid(signed word));
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.

View File

@ -0,0 +1,83 @@
// Tests conversion of numbers to correct int types
// See https://gitlab.com/camelot/kickc/issues/181
void main() {
// Signed type and number
// a) If one operand is a signed type and the other a number.
// The number becomes the smallest signed type that can hold its value (if one exists).
// The two operands are then converted normally (using 2a or 2b).
idx = 0ub;
assertType(typeid(12sb+12), typeid(signed byte));
assertType(typeid(12sb+130), typeid(signed word));
assertType(typeid(12sb+32000), typeid(signed dword));
assertType(typeid(12sw+12), typeid(signed word));
assertType(typeid(12sw+130), typeid(signed word));
assertType(typeid(12sw+100000), typeid(signed dword));
assertType(typeid(12sd+12), typeid(signed dword));
assertType(typeid(12sd+130), typeid(signed dword));
assertType(typeid(12sd+100000), typeid(signed dword));
// Test number larger than largest signed type
assertType(typeid(12sb+3000000000), typeid(unsigned dword));
// Unsigned type and positive number
// b) If one operand is an unsigned type and the other a positive number.
// The number is converted to the smallest unsigned type that can hold the value.
// The two operands are then converted normally (using 2a or 2b).
idx = 40ub;
assertType(typeid(12ub+12), typeid(unsigned byte));
assertType(typeid(12ub+250), typeid(unsigned byte));
assertType(typeid(12ub+300), typeid(unsigned word));
assertType(typeid(12ub+65534), typeid(unsigned word));
assertType(typeid(12ub+66000), typeid(unsigned dword));
assertType(typeid(12uw+12), typeid(unsigned word));
assertType(typeid(12uw+130), typeid(unsigned word));
assertType(typeid(12uw+66000), typeid(unsigned dword));
assertType(typeid(12ud+12), typeid(unsigned dword));
assertType(typeid(12ud+130), typeid(unsigned dword));
assertType(typeid(12ud+66000), typeid(unsigned dword));
assertType(typeid(12sb+3000000000), typeid(unsigned dword));
// Unsigned type and negative number
// If one operand is an unsigned type and the other a negative number.
// The number is first converted to the smallest signed type that can hold its value (if one exits).
// If the signed type is smaller than the unsigned type it is converted up to the size of the unsigned type.
// The signed type is finally converted to unsigned.
// The two unsigned operands are then finally converted to the size of the largest type (using 2b).
idx = 80ub;
assertType(typeid(12ub+-12), typeid(unsigned byte));
assertType(typeid(12ub+-120), typeid(unsigned byte));
assertType(typeid(12ub+-250), typeid(unsigned word));
assertType(typeid(12ub+-32000), typeid(unsigned dword));
assertType(typeid(12ub+-66000), typeid(unsigned dword));
assertType(typeid(12uw+-12), typeid(unsigned word));
assertType(typeid(12uw+-130), typeid(unsigned word));
assertType(typeid(12uw+-32000), typeid(unsigned dword));
assertType(typeid(12ud+-12), typeid(unsigned dword));
assertType(typeid(12ud+-130), typeid(unsigned dword));
assertType(typeid(12ud+-66000), typeid(unsigned dword));
assertType(typeid(12sb+-3000000000), typeid(unsigned dword));
}
const byte RED = 2ub;
const byte GREEN = 5ub;
const byte* SCREEN = $0400uw;
const byte* COLS = $d800uw;
byte idx = 0ub;
// 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;
} else {
COLS[idx] = RED;
}
SCREEN[idx++] = t1;
}

View File

@ -1,23 +1,44 @@
// 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);
testBytes();
testSBytes();
}
void testBytes() {
// Constant values resolvable to bytes
const byte* SCREEN = 0x0400;
byte idx = 0;
SCREEN[idx++] = 12;
SCREEN[idx++] = 6+6;
SCREEN[idx++] = 18-6;
SCREEN[idx++] = 1812-1800;
SCREEN[idx++] = 1+2+3+6;
SCREEN[idx++] = 2*6;
SCREEN[idx++] = 3<<2;
SCREEN[idx++] = 24>>1;
SCREEN[idx++] = 15&28;
SCREEN[idx++] = 4|8;
SCREEN[idx++] = 5^9;
SCREEN[idx++] = (2+2)*(15/5);
SCREEN[idx++] = (byte)(4096+12);
}
void testSBytes() {
// Constant values resolvable to signed bytes
const signed byte* SCREEN = 0x0428;
byte idx = 0;
SCREEN[idx++] = -12;
SCREEN[idx++] = -6-6;
SCREEN[idx++] = -18+6;
SCREEN[idx++] = -1812+1800;
SCREEN[idx++] = -1-2-3-6;
SCREEN[idx++] = -2*6;
SCREEN[idx++] = -3<<2;
SCREEN[idx++] = -24>>1;
SCREEN[idx++] = -4&-9;
SCREEN[idx++] = -0x10|-0xfc;
SCREEN[idx++] = (-2-2)*(15/5);
SCREEN[idx++] = (signed byte)(4096-12);
}