mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-01-08 13:31:03 +00:00
New literal word constructor handling - 277/354
This commit is contained in:
parent
07b67efb7c
commit
fe60b31f73
@ -254,6 +254,7 @@ public class Compiler {
|
||||
optimizations.add(new Pass2ConstantIfs(program));
|
||||
optimizations.add(new Pass2ConstantStringConsolidation(program));
|
||||
optimizations.add(new Pass2FixInlineConstructors(program));
|
||||
optimizations.add(new Pass2FixInlineConstructorsNew(program));
|
||||
optimizations.add(new Pass2TypeInference(program));
|
||||
optimizations.add(new PassNEliminateUnusedVars(program, true));
|
||||
optimizations.add(new Pass2EliminateRedundantCasts(program));
|
||||
|
@ -5,9 +5,7 @@ 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 dk.camelot64.kickc.model.values.ConstantUnary;
|
||||
import dk.camelot64.kickc.model.values.PointerDereferenceIndexed;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
@ -33,6 +31,8 @@ public class ProgramExpressionIterator {
|
||||
handler.execute(new ProgramExpressionUnary.ProgramExpressionUnaryConstant(programValue), null, null, null);
|
||||
} else if(programValue.get() instanceof PointerDereferenceIndexed) {
|
||||
handler.execute(new ProgramExpressionBinary.ProgramExpressionBinaryPointerDereferenceIndexed(programValue), null, null, null);
|
||||
} else if(programValue.get() instanceof ConstantCastValue) {
|
||||
handler.execute(new ProgramExpressionUnary.ProgramExpressionUnaryCast(programValue), null, null, null);
|
||||
}
|
||||
};
|
||||
ProgramValueIterator.execute(program.getScope(), programValueHandler);
|
||||
|
@ -1,7 +1,9 @@
|
||||
package dk.camelot64.kickc.model.iterator;
|
||||
|
||||
import dk.camelot64.kickc.model.operators.OperatorUnary;
|
||||
import dk.camelot64.kickc.model.operators.Operators;
|
||||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||
import dk.camelot64.kickc.model.values.ConstantCastValue;
|
||||
import dk.camelot64.kickc.model.values.ConstantUnary;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.Value;
|
||||
@ -52,7 +54,7 @@ public interface ProgramExpressionUnary extends ProgramExpression {
|
||||
|
||||
/** Unary expression as part of a constant expression. */
|
||||
class ProgramExpressionUnaryConstant implements ProgramExpressionUnary {
|
||||
|
||||
|
||||
/** A ProgramValue containing a {@link ConstantUnary}. */
|
||||
private ProgramValue programValue;
|
||||
|
||||
@ -81,4 +83,37 @@ public interface ProgramExpressionUnary extends ProgramExpression {
|
||||
|
||||
}
|
||||
|
||||
/** Unary cast expression {@link ConstantCastValue} as part of a constant expression. */
|
||||
class ProgramExpressionUnaryCast implements ProgramExpressionUnary {
|
||||
|
||||
/** A ProgramValue containing a {@link ConstantCastValue}. */
|
||||
private ProgramValue programValue;
|
||||
|
||||
ProgramExpressionUnaryCast(ProgramValue programValue) {
|
||||
this.programValue = programValue;
|
||||
}
|
||||
|
||||
public ConstantCastValue getConstantUnary() {
|
||||
return (ConstantCastValue) programValue.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperatorUnary getOperator() {
|
||||
return Operators.getCastUnary(getConstantUnary().getToType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RValue getOperand() {
|
||||
return getConstantUnary().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Value value) {
|
||||
programValue.set(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -31,11 +31,7 @@ public class SymbolTypeArray extends SymbolTypePointer {
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
SymbolType elementType = getElementType();
|
||||
if(elementType instanceof SymbolTypeMulti) {
|
||||
return "(" + elementType.getTypeName() + ")" + "[" + (size == null ? "" : size.toString()) + "]";
|
||||
} else {
|
||||
return elementType.getTypeName() + "[" + (size == null ? "" : size.toString()) + "]";
|
||||
}
|
||||
return elementType.getTypeName() + "[" + (size == null ? "" : size.toString()) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,10 +9,6 @@ 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Type inference of expressions (rValues & unary/binary operators)
|
||||
*/
|
||||
@ -126,7 +122,7 @@ public class SymbolTypeInference {
|
||||
} else if(rValue instanceof ProcedureRef) {
|
||||
Procedure procedure = symbols.getProcedure((ProcedureRef) rValue);
|
||||
return procedure.getType();
|
||||
} else if(rValue instanceof AssignmentRValue) {
|
||||
} else if(rValue instanceof AssignmentRValue) {
|
||||
return inferTypeRValue(symbols, ((AssignmentRValue) rValue).getAssignment());
|
||||
}
|
||||
if(type == null) {
|
||||
@ -148,21 +144,8 @@ public class SymbolTypeInference {
|
||||
}
|
||||
}
|
||||
if(elmType != null) {
|
||||
if((list.getList().size() == 2 && SymbolType.BYTE.equals(elmType) || SymbolType.SBYTE.equals(elmType))) {
|
||||
// Potentially a word constructor - return a composite type
|
||||
ArrayList<SymbolType> types = new ArrayList<>();
|
||||
types.add(new SymbolTypeArray(elmType));
|
||||
types.add(SymbolType.WORD);
|
||||
return new SymbolTypeMulti(types);
|
||||
} else if((list.getList().size() == 2 && SymbolType.WORD.equals(elmType))) {
|
||||
// Potentially a dword constructor - return a composite type
|
||||
ArrayList<SymbolType> types = new ArrayList<>();
|
||||
types.add(new SymbolTypeArray(elmType));
|
||||
types.add(SymbolType.DWORD);
|
||||
return new SymbolTypeMulti(types);
|
||||
} else {
|
||||
return new SymbolTypeArray(elmType);
|
||||
}
|
||||
long size = list.getList().size();
|
||||
return new SymbolTypeArray(elmType, new ConstantInteger(size, size<256?SymbolType.BYTE:SymbolType.WORD));
|
||||
} else {
|
||||
throw new RuntimeException("Cannot infer list element type " + list.toString());
|
||||
}
|
||||
@ -223,13 +206,8 @@ public class SymbolTypeInference {
|
||||
SymbolType valueType = inferType(programScope, rValue);
|
||||
if(type == null) {
|
||||
type = valueType;
|
||||
} else {
|
||||
SymbolType newType = intersectTypes(type, valueType);
|
||||
if(newType == null) {
|
||||
throw new CompileError("Types not compatible " + type + " and " + valueType);
|
||||
}
|
||||
type = newType;
|
||||
}
|
||||
} else if(!type.equals(valueType))
|
||||
throw new CompileError("Types not compatible " + type + " and " + valueType);
|
||||
}
|
||||
if(!SymbolType.VAR.equals(symbol.getType()) && !type.equals(symbol.getType())) {
|
||||
program.getLog().append("Inferred type updated to " + type + " for " + symbol.toString(program));
|
||||
@ -295,53 +273,4 @@ public class SymbolTypeInference {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the symbol type that is the intersection between the two passed types.
|
||||
* Handles SymbolTypeMulti by intersecting the sub type lists.
|
||||
*
|
||||
* @param type1 The first type
|
||||
* @param type2 The second type
|
||||
* @return The intersection between the two types (handling multi-types)
|
||||
*/
|
||||
public static SymbolType intersectTypes(SymbolType type1, SymbolType type2) {
|
||||
List<SymbolType> newSubTypes = new ArrayList<>();
|
||||
if(type1 instanceof SymbolTypeMulti) {
|
||||
Collection<SymbolType> subTypes1 = ((SymbolTypeMulti) type1).getTypes();
|
||||
if(type2 instanceof SymbolTypeMulti) {
|
||||
Collection<SymbolType> subTypes2 = ((SymbolTypeMulti) type2).getTypes();
|
||||
for(SymbolType subType1 : subTypes1) {
|
||||
if(subTypes2.contains(subType1)) {
|
||||
newSubTypes.add(subType1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Element type is not multi - check if the list type contains it
|
||||
if(subTypes1.contains(type2)) {
|
||||
newSubTypes.add(type2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// List-type not multi - check if the element type contains it
|
||||
if(type2 instanceof SymbolTypeMulti) {
|
||||
Collection<SymbolType> subTypes2 = ((SymbolTypeMulti) type2).getTypes();
|
||||
if(subTypes2.contains(type1)) {
|
||||
newSubTypes.add(type1);
|
||||
}
|
||||
} else {
|
||||
// Element type is not multi - check if the list type is the same
|
||||
if(type1.equals(type2)) {
|
||||
newSubTypes.add(type1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(newSubTypes.size() == 0) {
|
||||
return null;
|
||||
} else if(newSubTypes.size() == 1) {
|
||||
// A single type matching - use it
|
||||
return newSubTypes.get(0);
|
||||
} else {
|
||||
// Multiple matches was found - use them
|
||||
return new SymbolTypeMulti(newSubTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,4 +38,8 @@ public class ValueList implements RValue {
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(null);
|
||||
}
|
||||
}
|
||||
|
@ -53,37 +53,6 @@ public class Pass2ConstantCastSimplification extends Pass2SsaOptimization {
|
||||
}
|
||||
}
|
||||
});
|
||||
ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> {
|
||||
if(programValue.get() instanceof ConstantCastValue) {
|
||||
ConstantCastValue constantCastValue = (ConstantCastValue) programValue.get();
|
||||
if(constantCastValue.getValue() instanceof ConstantInteger) {
|
||||
ConstantInteger constantInteger = (ConstantInteger) constantCastValue.getValue();
|
||||
SymbolType castType = constantCastValue.getToType();
|
||||
if(SymbolType.NUMBER.equals(constantInteger.getType())) {
|
||||
if(castType instanceof SymbolTypeIntegerFixed ) {
|
||||
ConstantInteger newConstInt = new ConstantInteger(constantInteger.getInteger(), castType);
|
||||
programValue.set(newConstInt);
|
||||
getLog().append("Simplifying constant integer cast " + newConstInt.toString());
|
||||
optimized.set(true);
|
||||
} else if(castType instanceof SymbolTypePointer) {
|
||||
ConstantPointer newConstPointer = new ConstantPointer(constantInteger.getInteger(), ((SymbolTypePointer) castType).getElementType());
|
||||
programValue.set(newConstPointer);
|
||||
getLog().append("Simplifying constant pointer cast " + newConstPointer.toString());
|
||||
optimized.set(true);
|
||||
}
|
||||
} else if(castType instanceof SymbolTypeIntegerFixed) {
|
||||
if(((SymbolTypeIntegerFixed) castType).contains(constantInteger.getValue())) {
|
||||
// Cast type contains the value - cast not needed
|
||||
ConstantInteger newConstInt = new ConstantInteger(constantInteger.getInteger(), castType);
|
||||
programValue.set(newConstInt);
|
||||
getLog().append("Simplifying constant integer cast " + newConstInt.toString());
|
||||
optimized.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return optimized.get();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
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.Operators;
|
||||
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.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.CastValue;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.ValueList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Identifies word constructors <code> (word) { b1, b2 }</code> and replaces
|
||||
* them with a binary operator <code>word w = b1 w= b2 ;</code>
|
||||
*/
|
||||
public class Pass2FixInlineConstructorsNew extends Pass2SsaOptimization {
|
||||
|
||||
public Pass2FixInlineConstructorsNew(Program program) {
|
||||
super(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean step() {
|
||||
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());
|
||||
optimized.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return optimized.get();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ 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.*;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -37,6 +38,15 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization {
|
||||
binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getScope());
|
||||
modified.set(true);
|
||||
}
|
||||
|
||||
// Detect word literal constructor
|
||||
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());
|
||||
modified.set(true);
|
||||
}
|
||||
|
||||
}
|
||||
if(leftType instanceof SymbolTypeIntegerFixed && SymbolType.isInteger(rightType)) {
|
||||
SymbolType conversionType = SymbolTypeConversion.convertedMathType((SymbolTypeInteger) leftType, (SymbolTypeInteger) rightType);
|
||||
@ -53,5 +63,14 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization {
|
||||
return modified.get();
|
||||
}
|
||||
|
||||
public static boolean isLiteralWordCandidate(SymbolType rightType) {
|
||||
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()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1359,6 +1359,21 @@ public class TestPrograms {
|
||||
compileAndCompare("inline-assignment");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineWord0() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-word-0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineWord1() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-word-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineWord2() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-word-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineWord() throws IOException, URISyntaxException {
|
||||
compileAndCompare("inline-word");
|
||||
|
7
src/test/kc/inline-word-0.kc
Normal file
7
src/test/kc/inline-word-0.kc
Normal file
@ -0,0 +1,7 @@
|
||||
// Tests minimal inline word
|
||||
|
||||
void main() {
|
||||
word w = { 0x02ub, 0x01ub };
|
||||
word* screen = 0x0400;
|
||||
screen[0] = w;
|
||||
}
|
7
src/test/kc/inline-word-1.kc
Normal file
7
src/test/kc/inline-word-1.kc
Normal file
@ -0,0 +1,7 @@
|
||||
// Tests minimal inline word
|
||||
|
||||
void main() {
|
||||
word w = { 0x01, 0x02 };
|
||||
word* screen = 0x0400;
|
||||
screen[0] = w;
|
||||
}
|
7
src/test/kc/inline-word-2.kc
Normal file
7
src/test/kc/inline-word-2.kc
Normal file
@ -0,0 +1,7 @@
|
||||
// Tests minimal inline word
|
||||
|
||||
void main() {
|
||||
word w = { 0x01, 0x02ub };
|
||||
word* screen = 0x0400;
|
||||
screen[0] = w;
|
||||
}
|
Loading…
Reference in New Issue
Block a user