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:
parent
fe60b31f73
commit
3d56f3705a
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
6
src/test/kc/inline-pointer-0.kc
Normal file
6
src/test/kc/inline-pointer-0.kc
Normal file
@ -0,0 +1,6 @@
|
||||
// Tests creating a literal pointer from two bytes
|
||||
|
||||
void main() {
|
||||
byte* screen = (byte*) { 4, 0 };
|
||||
*screen = 'a';
|
||||
}
|
11
src/test/kc/inline-pointer-1.kc
Normal file
11
src/test/kc/inline-pointer-1.kc
Normal 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';
|
||||
}
|
6
src/test/kc/inline-pointer-2.kc
Normal file
6
src/test/kc/inline-pointer-2.kc
Normal 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';
|
||||
}
|
9
src/test/kc/mixed-array-0.kc
Normal file
9
src/test/kc/mixed-array-0.kc
Normal 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];
|
||||
}
|
@ -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'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user