1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-20 02:32:36 +00:00

Array initializer with number handling - 278/358

This commit is contained in:
jespergravgaard 2019-05-13 09:17:30 +02:00
parent fe60b31f73
commit 3d56f3705a
13 changed files with 200 additions and 31 deletions

View File

@ -233,6 +233,7 @@ public class Compiler {
List<Pass2SsaOptimization> optimizations = new ArrayList<>();
optimizations.add(new PassNAddTypeConversionAssignment(program));
optimizations.add(new PassNAddNumberTypeConversions(program));
optimizations.add(new PassNAddArrayNumberTypeConversions(program));
optimizations.add(new PassNDowngradeBytePlusWord(program));
optimizations.add(new PassNTypeInference(program));
optimizations.add(new PassNTypeIdSimplification(program));
@ -253,8 +254,9 @@ public class Compiler {
//optimizations.add(new Pass2ConstantAdditionElimination(program));
optimizations.add(new Pass2ConstantIfs(program));
optimizations.add(new Pass2ConstantStringConsolidation(program));
optimizations.add(new Pass2FixInlineConstructors(program));
//optimizations.add(new Pass2FixInlineConstructors(program));
optimizations.add(new Pass2FixInlineConstructorsNew(program));
optimizations.add(new PassNAddTypeConversionAssignment(program));
optimizations.add(new Pass2TypeInference(program));
optimizations.add(new PassNEliminateUnusedVars(program, true));
optimizations.add(new Pass2EliminateRedundantCasts(program));

View File

@ -169,7 +169,13 @@ public interface ProgramExpressionBinary extends ProgramExpression {
if(assignment.getrValue1() == null && assignment.getOperator() == null) {
assignment.setOperator(Operators.getCastUnary(toType));
} else {
throw new InternalError("Not implemented!");
Scope blockScope = symbols.getScope(currentScope);
VariableIntermediate tmpVar = blockScope.addVariableIntermediate();
SymbolType rightType = SymbolTypeInference.inferType(symbols, getRight());
tmpVar.setType(rightType);
StatementAssignment newAssignment = new StatementAssignment(assignment.getlValue(), Operators.getCastUnary(toType), tmpVar.getRef(), assignment.getSource(), Comment.NO_COMMENTS);
assignment.setlValue(tmpVar.getRef());
stmtIt.add(newAssignment);
}
}

View File

@ -139,7 +139,13 @@ public class SymbolTypeInference {
elmType = type;
} else {
if(!elmType.equals(type)) {
throw new RuntimeException("Array element has type mismatch " + elm.toString() + " not matching type " + elmType.getTypeName());
if(SymbolType.NUMBER.equals(elmType) && SymbolType.isInteger(type)) {
elmType = SymbolType.NUMBER;
} else if(SymbolType.isInteger(elmType) && SymbolType.NUMBER.equals(type)) {
elmType = SymbolType.NUMBER;
} else {
throw new CompileError("Array element has type mismatch "+elm.toString() + " not matching type " + elmType.getTypeName());
}
}
}
}

View File

@ -98,7 +98,7 @@ public class Pass2ConstantRValueConsolidation extends Pass2SsaOptimization {
} 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()));
throw new RuntimeException("Array type " + listType + " does not match element type " + elmType + ". Array: " + valueList.toString(getProgram()));
}
}
elements.add(constantValue);

View File

@ -1,11 +1,15 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramExpression;
import dk.camelot64.kickc.model.iterator.ProgramExpressionIterator;
import dk.camelot64.kickc.model.iterator.ProgramExpressionUnary;
import dk.camelot64.kickc.model.operators.OperatorBinary;
import dk.camelot64.kickc.model.operators.OperatorCastPtr;
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;
@ -15,6 +19,7 @@ import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.ValueList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@ -32,29 +37,19 @@ public class Pass2FixInlineConstructorsNew extends Pass2SsaOptimization {
AtomicBoolean optimized = new AtomicBoolean(false);
ProgramExpressionIterator.execute(getProgram(), (programExpression, currentStmt, stmtIt, currentBlock) -> {
if(programExpression instanceof ProgramExpressionUnary) {
if(programExpression.getOperator().equals(Operators.CAST_WORD)) {
if(((ProgramExpressionUnary) programExpression).getOperand() instanceof ValueList) {
ValueList list = (ValueList) ((ProgramExpressionUnary) programExpression).getOperand();
List<RValue> listValues = list.getList();
if(listValues.size()==2) {
OperatorBinary constructOperator = Operators.WORD;
SymbolType constructType = SymbolType.WORD;
// Convert list to a word constructor in a new tmp variable
Scope currentScope = Pass2FixInlineConstructorsNew.this.getScope().getScope(currentBlock.getScope());
VariableIntermediate tmpVar = currentScope.addVariableIntermediate();
tmpVar.setTypeInferred(constructType);
// Move backward - to insert before the current statement
stmtIt.previous();
// Add assignment of the new tmpVar
StatementAssignment assignment = new StatementAssignment(tmpVar.getRef(), new CastValue(SymbolType.BYTE, listValues.get(0)), constructOperator, new CastValue(SymbolType.BYTE, listValues.get(1)), currentStmt.getSource(), Comment.NO_COMMENTS);
stmtIt.add(assignment);
// Move back before the current statement
stmtIt.next();
// Replace current value with the reference
programExpression.set(tmpVar.getRef());
Pass2FixInlineConstructorsNew.this.getLog().append("Fixing inline constructor with " + assignment.toString());
if(((ProgramExpressionUnary) programExpression).getOperand() instanceof ValueList) {
ValueList list = (ValueList) ((ProgramExpressionUnary) programExpression).getOperand();
List<RValue> listValues = list.getList();
if(listValues.size() == 2) {
if(programExpression.getOperator().equals(Operators.CAST_WORD)) {
addLiteralWordConstructor(Operators.WORD, SymbolType.WORD, SymbolType.BYTE, programExpression, listValues, currentStmt, stmtIt, currentBlock);
optimized.set(true);
} else if(programExpression.getOperator() instanceof OperatorCastPtr) {
SymbolType castType = ((OperatorCastPtr) programExpression.getOperator()).getToType();
addLiteralWordConstructor(Operators.WORD, castType, SymbolType.BYTE, programExpression, listValues, currentStmt, stmtIt, currentBlock);
optimized.set(true);
} else if(programExpression.getOperator().equals(Operators.CAST_DWORD)) {
addLiteralWordConstructor(Operators.DWORD, SymbolType.DWORD, SymbolType.WORD, programExpression, listValues, currentStmt, stmtIt, currentBlock);
optimized.set(true);
}
}
@ -65,5 +60,35 @@ public class Pass2FixInlineConstructorsNew extends Pass2SsaOptimization {
}
/**
* Add a literal word/dword constructor.
* Converts a cast value-list with 2 items to a word/dword constructor.
* (word) { 1, 2 } -> 1 =w 2
* @param constructOperator The operator to add
* @param constructType The type being constructed
* @param subType The sub-type
* @param programExpression The program expression containing the cast value list
* @param listValues
* @param currentStmt
* @param stmtIt
* @param currentBlock
*/
public void addLiteralWordConstructor(OperatorBinary constructOperator, SymbolType constructType, SymbolType subType, ProgramExpression programExpression, List<RValue> listValues, Statement currentStmt, ListIterator<Statement> stmtIt, ControlFlowBlock currentBlock) {
// Convert list to a word constructor in a new tmp variable
Scope currentScope = Pass2FixInlineConstructorsNew.this.getScope().getScope(currentBlock.getScope());
VariableIntermediate tmpVar = currentScope.addVariableIntermediate();
tmpVar.setTypeInferred(constructType);
// Move backward - to insert before the current statement
stmtIt.previous();
// Add assignment of the new tmpVar
StatementAssignment assignment = new StatementAssignment(tmpVar.getRef(), new CastValue(subType, listValues.get(0)), constructOperator, new CastValue(subType, listValues.get(1)), currentStmt.getSource(), Comment.NO_COMMENTS);
stmtIt.add(assignment);
// Move back before the current statement
stmtIt.next();
// Replace current value with the reference
programExpression.set(tmpVar.getRef());
Pass2FixInlineConstructorsNew.this.getLog().append("Fixing inline constructor with " + assignment.toString());
}
}

View File

@ -0,0 +1,63 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramExpressionBinary;
import dk.camelot64.kickc.model.iterator.ProgramExpressionIterator;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeArray;
import dk.camelot64.kickc.model.types.SymbolTypeConversion;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Add casts to {@link SymbolType#NUMBER} expressions that are inside array initializers based on the type of the array
*/
public class PassNAddArrayNumberTypeConversions extends Pass2SsaOptimization {
public PassNAddArrayNumberTypeConversions(Program program) {
super(program);
}
@Override
public boolean step() {
AtomicBoolean modified = new AtomicBoolean(false);
ProgramExpressionIterator.execute(getProgram(), (binaryExpression, currentStmt, stmtIt, currentBlock) -> {
if(binaryExpression instanceof ProgramExpressionBinary) {
ProgramExpressionBinary binary = (ProgramExpressionBinary) binaryExpression;
RValue left = binary.getLeft();
RValue right = binary.getRight();
SymbolType leftType = SymbolTypeInference.inferType(getProgram().getScope(), left);
if(leftType instanceof SymbolTypeArray && right instanceof ValueList) {
SymbolType declaredElmType = ((SymbolTypeArray) leftType).getElementType();
ListIterator<RValue> elmIterator = ((ValueList) right).getList().listIterator();
while(elmIterator.hasNext()) {
RValue elm = elmIterator.next();
SymbolType elmType = SymbolTypeInference.inferType(getProgram().getScope(), elm);
if(SymbolType.NUMBER.equals(elmType)) {
// Add a cast - if the value fits.
if(!SymbolTypeConversion.assignmentTypeMatch(declaredElmType, elmType)) {
throw new CompileError("Array type " + declaredElmType + " does not match element type " + elmType , currentStmt);
}
// TODO: Test if the value matches the declared type!
if(elm instanceof ConstantValue) {
elmIterator.set(new ConstantCastValue(declaredElmType, (ConstantValue) elm));
} else {
elmIterator.set(new CastValue(declaredElmType, elm));
}
modified.set(true);
}
}
if(modified.get()) {
getLog().append("Adding number conversion cast (" + declaredElmType+ ") to elements in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false)));
}
}
}
});
return modified.get();
}
}

View File

@ -43,7 +43,22 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization {
if(leftType.equals(SymbolType.WORD) && isLiteralWordCandidate(rightType)) {
SymbolType conversionType = SymbolType.WORD;
getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false)));
binary.addRightCast(SymbolType.WORD, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getScope());
binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getScope());
modified.set(true);
}
// Detect word literal constructor
if(leftType instanceof SymbolTypePointer && isLiteralWordCandidate(rightType)) {
SymbolType conversionType = SymbolType.WORD;
getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false)));
binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getScope());
modified.set(true);
}
// Detect dword literal constructor
if(leftType.equals(SymbolType.DWORD) && isLiteralWordCandidate(rightType)) {
SymbolType conversionType = SymbolType.DWORD;
getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false)));
binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getScope());
modified.set(true);
}
@ -67,7 +82,7 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization {
if(rightType instanceof SymbolTypeArray) {
SymbolTypeArray rightArray = (SymbolTypeArray) rightType;
if(new ConstantInteger(2L, SymbolType.BYTE).equals(rightArray.getSize()))
if(SymbolType.BYTE.equals(rightArray.getElementType()) || SymbolType.NUMBER.equals(rightArray.getElementType()))
if(SymbolType.isInteger(rightArray.getElementType()))
return true;
}
return false;

View File

@ -32,9 +32,29 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testMixedArray0() throws IOException, URISyntaxException {
compileAndCompare("mixed-array-0");
}
@Test
public void testInlinePointer2() throws IOException, URISyntaxException {
compileAndCompare("inline-pointer-2");
}
@Test
public void testInlinePointer1() throws IOException, URISyntaxException {
compileAndCompare("inline-pointer-1");
}
@Test
public void testInlinePointer0() throws IOException, URISyntaxException {
compileAndCompare("inline-pointer-0");
}
@Test
public void testToD018Problem() throws IOException, URISyntaxException {
compileAndCompare("tod018-problem", log());
compileAndCompare("tod018-problem");
}
@Test

View File

@ -0,0 +1,6 @@
// Tests creating a literal pointer from two bytes
void main() {
byte* screen = (byte*) { 4, 0 };
*screen = 'a';
}

View File

@ -0,0 +1,11 @@
// Tests creating a literal pointer from two bytes
void main() {
puta(4, 0x00);
puta(5, 0x18);
}
void puta(byte ph, byte pl) {
byte* screen = (byte*) { ph, pl };
*screen = 'a';
}

View File

@ -0,0 +1,6 @@
// Tests creating a literal pointer from two bytes
void main() {
byte* screen = (byte*){ 4, 0 } + (word){ 0, 40 };
*screen = 'a';
}

View File

@ -0,0 +1,9 @@
// Test an array with mixed byte/number types
void main() {
byte[] msg = { 1ub, 2, 3 };
byte* SCREEN = 0x400;
SCREEN[0] = msg[0];
SCREEN[1] = msg[1];
SCREEN[2] = msg[2];
}

View File

@ -3,7 +3,7 @@ void main() {
byte[] bs = { 'c', 'm' }; // constant byte array
byte b = 4; // constant byte
word w = { b, 0 }; // constant inline word
word w2 = { 1, 1 } + w + { 0, 0 }; // constant inline words inside expression
word w2 = (word){ 1, 1 } + w + (word){ 0, 0 }; // constant inline words inside expression
byte* sc = w2; // implicit cast to (byte*)
*sc = bs[1]; // In the end $501 is set to 'c'