1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-05 21:30:52 +00:00

Implemented interrupt procedures and pointers to procedures initial support.

This commit is contained in:
Jesper Gravgaard 2018-08-05 13:33:23 +02:00
parent c0c7a1da44
commit f463606742
38 changed files with 1344 additions and 411 deletions

View File

@ -2,11 +2,10 @@ package dk.camelot64.kickc.fragment;
import dk.camelot64.kickc.model.CompileError;
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.operators.*;
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;
@ -54,10 +53,16 @@ public class AsmFormat {
(parenthesis ? ")" : "");
} else if(value instanceof ConstantPointer) {
return getAsmNumber(((ConstantPointer) value).getValue());
} else if(value instanceof ConstantVarPointer) {
VariableRef toVar = ((ConstantVarPointer) value).getToVar();
Variable variable = program.getScope().getVariable(toVar);
return getAsmParamName(variable, codeScope);
} else if(value instanceof ConstantSymbolPointer) {
SymbolRef toSym = ((ConstantSymbolPointer) value).getToSymbol();
Symbol symbol = program.getScope().getSymbol(toSym);
if(symbol instanceof Variable) {
return getAsmParamName((Variable) symbol, codeScope);
} else if(symbol instanceof Procedure) {
return getAsmParamName((Procedure) symbol, codeScope);
} else {
throw new RuntimeException("Unhandled symbol type "+symbol);
}
} else if(value instanceof ConstantCastValue) {
ConstantCastValue castValue = (ConstantCastValue) value;
OperatorUnary castOperator = Operators.getCastUnary(castValue.getToType());
@ -110,7 +115,7 @@ public class AsmFormat {
} else {
return getAsmConstant(program, new ConstantBinary(new ConstantInteger((long)0xff), Operators.BOOL_AND, operand), outerPrecedence, codeScope);
}
} else if(Operators.CAST_WORD.equals(operator) || Operators.CAST_SWORD.equals(operator) || Operators.CAST_PTRBY.equals(operator)|| Operators.CAST_PTRSBY.equals(operator)|| Operators.CAST_PTRWO.equals(operator)|| Operators.CAST_PTRSWO.equals(operator)|| Operators.CAST_PTRDWO.equals(operator)|| Operators.CAST_PTRSDWO.equals(operator)|| Operators.CAST_PTRBO.equals(operator)) {
} else if(operator instanceof OperatorCastPtr || Operators.CAST_WORD.equals(operator) || Operators.CAST_SWORD.equals(operator) || Operators.CAST_PTRBY.equals(operator)|| Operators.CAST_PTRSBY.equals(operator)|| Operators.CAST_PTRWO.equals(operator)|| Operators.CAST_PTRSWO.equals(operator)|| Operators.CAST_PTRDWO.equals(operator)|| Operators.CAST_PTRSDWO.equals(operator)|| Operators.CAST_PTRBO.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) {
// No cast needed
@ -230,4 +235,17 @@ public class AsmFormat {
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
}
/**
* Get the ASM parameter for a specific bound constant
*
* @param boundProc The constant
* @return The ASM parameter to use in the ASM code
*/
private static String getAsmParamName(Procedure boundProc, ScopeRef codeScopeRef) {
ScopeRef procScopeRef = boundProc.getScope().getRef();
String asmName = boundProc.getLocalName();
return getAsmParamName(procScopeRef, asmName, codeScopeRef);
}
}

View File

@ -15,6 +15,7 @@ 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.SymbolTypeProcedure;
import dk.camelot64.kickc.model.values.*;
import java.util.LinkedHashMap;
@ -322,6 +323,10 @@ public class AsmFragmentInstanceSpec {
return "pws";
} else if(SymbolType.BOOLEAN.equals(elementType)) {
return "pbo";
} else if(elementType instanceof SymbolTypeProcedure) {
return "ppr";
} else if(elementType instanceof SymbolTypePointer) {
return "ppt";
} else {
throw new RuntimeException("Not implemented " + type);
}

View File

@ -0,0 +1,4 @@
lda #<{c2}
sta {c1}
lda #>{c2}
sta {c1}+1

View File

@ -361,22 +361,22 @@ public abstract class ProgramValue {
/**
* Pointer inside a variable pointer.
*/
public static class VarPointer extends ProgramValue {
private final ConstantVarPointer varPointer;
public static class ConstantSymbolPointerTo extends ProgramValue {
private final ConstantSymbolPointer varPointer;
VarPointer(ConstantVarPointer varPointer) {
ConstantSymbolPointerTo(ConstantSymbolPointer varPointer) {
this.varPointer = varPointer;
}
@Override
public RValue get() {
return varPointer.getToVar();
return (RValue) varPointer.getToSymbol();
}
@Override
public void set(RValue val) {
varPointer.setToVar((VariableRef) val);
varPointer.setToSymbol((VariableRef) val);
}
}

View File

@ -178,8 +178,8 @@ public class ProgramValueIterator {
subValues.add(new ProgramValue.CastValue((CastValue) value));
} else if(value instanceof ConstantCastValue) {
subValues.add(new ProgramValue.ConstantCastValue((ConstantCastValue) value));
} else if(value instanceof ConstantVarPointer) {
subValues.add(new ProgramValue.VarPointer((ConstantVarPointer) value));
} else if(value instanceof ConstantSymbolPointer) {
subValues.add(new ProgramValue.ConstantSymbolPointerTo((ConstantSymbolPointer) value));
} else if(value instanceof RangeValue) {
subValues.add(new ProgramValue.RangeFirst((RangeValue) value));
subValues.add(new ProgramValue.RangeLast((RangeValue) value));
@ -194,6 +194,7 @@ public class ProgramValueIterator {
subValues.add(new ProgramValue.ConstantArrayFilledSize((ConstantArrayFilled) value));
} else if(value == null ||
value instanceof VariableRef ||
value instanceof ProcedureRef ||
value instanceof ConstantLiteral ||
value instanceof ConstantRef ||
value instanceof LvalueIntermediate

View File

@ -0,0 +1,34 @@
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.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeSimple;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
import dk.camelot64.kickc.model.values.ConstantPointer;
/** Unary Cast to a pointer ( type* ) */
public class OperatorCastPtr extends OperatorUnary {
private final SymbolType elementType;
public OperatorCastPtr(int precedence, SymbolType elementType) {
super("((" + elementType.toString() + "*))", "_ptr_", precedence);
this.elementType = elementType;
}
@Override
public ConstantLiteral calculateLiteral(ConstantLiteral value) {
if(value instanceof ConstantInteger) {
return new ConstantPointer(((ConstantInteger) value).getInteger(), elementType);
}
throw new CompileError("Calculation not implemented " + getOperator() + " " + value);
}
@Override
public SymbolType inferType(SymbolTypeSimple operandType) {
return new SymbolTypePointer(elementType);
}
}

View File

@ -0,0 +1,32 @@
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.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeProcedure;
import dk.camelot64.kickc.model.types.SymbolTypeSimple;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantLiteral;
import dk.camelot64.kickc.model.values.ConstantPointer;
/** Unary Cast to procedure pointer operator ( (void()*) x ) */
public class OperatorCastPtrProc extends OperatorUnary {
public OperatorCastPtrProc(int precedence) {
super("((void()*))", "_ptrproc_", precedence);
}
@Override
public ConstantLiteral calculateLiteral(ConstantLiteral value) {
if(value instanceof ConstantInteger) {
return new ConstantPointer(((ConstantInteger) value).getInteger(), new SymbolTypeProcedure(SymbolType.VOID));
}
throw new CompileError("Calculation not implemented " + getOperator() + " " + value);
}
@Override
public SymbolType inferType(SymbolTypeSimple operandType) {
return new SymbolTypePointer(new SymbolTypeProcedure(SymbolType.VOID));
}
}

View File

@ -32,6 +32,7 @@ public class Operators {
public static final OperatorUnary CAST_PTRDWO = new OperatorCastPtrDWord(2);
public static final OperatorUnary CAST_PTRSDWO = new OperatorCastPtrSignedDWord(2);
public static final OperatorUnary CAST_PTRBO = new OperatorCastPtrBool(2);
public static final OperatorUnary CAST_PTRPROC = new OperatorCastPtrProc(2);
public static final OperatorUnary CAST_BOOL= new OperatorCastBool(2);
public static final OperatorBinary MULTIPLY = new OperatorMultiply(3);
public static final OperatorBinary DIVIDE = new OperatorDivide(3);
@ -169,6 +170,8 @@ public class Operators {
return CAST_PTRSDWO;
} else if(castType instanceof SymbolTypePointer && SymbolType.BOOLEAN.equals(((SymbolTypePointer) castType).getElementType())) {
return CAST_PTRBO;
} else if(castType instanceof SymbolTypePointer) {
return new OperatorCastPtr(CAST_BYTE.getPrecedence(), ((SymbolTypePointer) castType).getElementType());
} else {
throw new RuntimeException("Unknown cast type " + castType);

View File

@ -176,8 +176,8 @@ public class SymbolTypeInference {
return new SymbolTypeArray(((ArrayFilled) rValue).getElementType(), ((ArrayFilled) rValue).getSize());
} else if(rValue instanceof ConstantArrayFilled) {
return new SymbolTypeArray(((ConstantArrayFilled) rValue).getElementType(), ((ConstantArrayFilled) rValue).getSize());
} else if(rValue instanceof ConstantVarPointer) {
return ((ConstantVarPointer) rValue).getType(symbols);
} else if(rValue instanceof ConstantSymbolPointer) {
return ((ConstantSymbolPointer) rValue).getType(symbols);
} else if(rValue instanceof CastValue) {
return ((CastValue) rValue).getToType();
} else if(rValue instanceof ConstantCastValue) {
@ -188,6 +188,9 @@ public class SymbolTypeInference {
return ((RangeComparison) rValue).getType();
} else if(rValue instanceof RangeNext) {
return SymbolType.BYTE;
} else if(rValue instanceof ProcedureRef) {
Procedure procedure = symbols.getProcedure((ProcedureRef) rValue);
return procedure.getType();
}
if(type == null) {
throw new RuntimeException("Cannot infer type for " + rValue.toString());
@ -382,6 +385,7 @@ 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)
@ -417,12 +421,12 @@ public class SymbolTypeInference {
}
}
}
if(newSubTypes.size()==0) {
return null;
} else if(newSubTypes.size()==1) {
if(newSubTypes.size() == 0) {
return null;
} else if(newSubTypes.size() == 1) {
// A single type matching - use it
return newSubTypes.get(0);
} else {
} else {
// Multiple matches was found - use them
return new SymbolTypeMulti(newSubTypes);
}

View File

@ -31,9 +31,7 @@ public class SymbolTypePointer implements SymbolTypeSimple {
if(o == null || getClass() != o.getClass()) {
return false;
}
SymbolTypePointer that = (SymbolTypePointer) o;
return elementType != null ? elementType.equals(that.elementType) : that.elementType == null;
}

View File

@ -1,5 +1,7 @@
package dk.camelot64.kickc.model.types;
import java.util.Objects;
/** A function returning another type */
public class SymbolTypeProcedure implements SymbolTypeSimple {
@ -23,4 +25,17 @@ public class SymbolTypeProcedure implements SymbolTypeSimple {
return getTypeName();
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
SymbolTypeProcedure that = (SymbolTypeProcedure) o;
return Objects.equals(returnType, that.returnType);
}
@Override
public int hashCode() {
return Objects.hash(returnType);
}
}

View File

@ -2,31 +2,31 @@ package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
/** A pointer to a variable */
public class ConstantVarPointer implements ConstantValue {
/** A pointer to a symbol (variable or procedure) */
public class ConstantSymbolPointer implements ConstantValue {
/** The variable pointed to. */
private VariableRef toVar;
private SymbolRef toSymbol;
public ConstantVarPointer(VariableRef toVar) {
this.toVar = toVar;
public ConstantSymbolPointer(SymbolRef toSymbol) {
this.toSymbol = toSymbol;
}
public VariableRef getToVar() {
return toVar;
public SymbolRef getToSymbol() {
return toSymbol;
}
public void setToVar(VariableRef toVar) {
this.toVar = toVar;
public void setToSymbol(SymbolRef toSymbol) {
this.toSymbol = toSymbol;
}
@Override
public SymbolType getType(ProgramScope scope) {
Variable to = scope.getVariable(toVar);
Symbol to = scope.getSymbol(toSymbol);
return new SymbolTypePointer(to.getType());
}
@ -37,7 +37,7 @@ public class ConstantVarPointer implements ConstantValue {
@Override
public String toString(Program program) {
return "&" + toVar.toString(program);
return "&" + toSymbol.toString(program);
}
@Override

View File

@ -22,6 +22,6 @@ public class ForwardVariableRef implements RValue {
@Override
public String toString(Program program) {
return null;
return name;
}
}

View File

@ -1,7 +1,7 @@
package dk.camelot64.kickc.model.values;
/** A reference to a procedure */
public class ProcedureRef extends ScopeRef {
public class ProcedureRef extends ScopeRef implements RValue {
public ProcedureRef(String fullName) {
super(fullName);

View File

@ -94,10 +94,12 @@ forIteration
;
typeDecl
: SIMPLETYPE #typeSimple
: '(' typeDecl ')' #typePar
| SIMPLETYPE #typeSimple
| 'signed' SIMPLETYPE #typeSignedSimple
| typeDecl '*' #typePtr
| typeDecl '[' (expr)? ']' #typeArray
| typeDecl '(' ')' #typeProcedure
;
expr

View File

@ -1,4 +1,4 @@
// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.ParserRuleContext;
@ -467,6 +467,30 @@ public class KickCBaseListener implements KickCListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitForRange(KickCParser.ForRangeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterTypePar(KickCParser.TypeParContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitTypePar(KickCParser.TypeParContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterTypeProcedure(KickCParser.TypeProcedureContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitTypeProcedure(KickCParser.TypeProcedureContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -1,4 +1,4 @@
// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
@ -277,6 +277,20 @@ public class KickCBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitForRange(KickCParser.ForRangeContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypePar(KickCParser.TypeParContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitTypeProcedure(KickCParser.TypeProcedureContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -1,4 +1,4 @@
// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;

View File

@ -1,4 +1,4 @@
// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.ParseTreeListener;
@ -437,6 +437,30 @@ public interface KickCListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitForRange(KickCParser.ForRangeContext ctx);
/**
* Enter a parse tree produced by the {@code typePar}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
*/
void enterTypePar(KickCParser.TypeParContext ctx);
/**
* Exit a parse tree produced by the {@code typePar}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
*/
void exitTypePar(KickCParser.TypeParContext ctx);
/**
* Enter a parse tree produced by the {@code typeProcedure}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
*/
void enterTypeProcedure(KickCParser.TypeProcedureContext ctx);
/**
* Exit a parse tree produced by the {@code typeProcedure}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
*/
void exitTypeProcedure(KickCParser.TypeProcedureContext ctx);
/**
* Enter a parse tree produced by the {@code typePtr}
* labeled alternative in {@link KickCParser#typeDecl}.

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Generated from /Users/jespergravgaard/c64/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
// Generated from C:/c64/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
@ -263,6 +263,20 @@ public interface KickCVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitForRange(KickCParser.ForRangeContext ctx);
/**
* Visit a parse tree produced by the {@code typePar}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypePar(KickCParser.TypeParContext ctx);
/**
* Visit a parse tree produced by the {@code typeProcedure}
* labeled alternative in {@link KickCParser#typeDecl}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitTypeProcedure(KickCParser.TypeProcedureContext ctx);
/**
* Visit a parse tree produced by the {@code typePtr}
* labeled alternative in {@link KickCParser#typeDecl}.

View File

@ -13,6 +13,7 @@ import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeArray;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeProcedure;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.parser.KickCBaseVisitor;
import dk.camelot64.kickc.parser.KickCParser;
@ -639,6 +640,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
return SymbolType.get("signed " + ctx.SIMPLETYPE().getText());
}
@Override
public SymbolType visitTypePar(KickCParser.TypeParContext ctx) {
return (SymbolType) visit(ctx.typeDecl());
}
@Override
public SymbolType visitTypePtr(KickCParser.TypePtrContext ctx) {
SymbolType elementType = (SymbolType) visit(ctx.typeDecl());
@ -656,6 +662,12 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
@Override
public Object visitTypeProcedure(KickCParser.TypeProcedureContext ctx) {
SymbolType returnType = (SymbolType) visit(ctx.typeDecl());
return new SymbolTypeProcedure(returnType);
}
@Override
public Object visitExprAssignment(KickCParser.ExprAssignmentContext ctx) {
Object val = visit(ctx.expr(0));
@ -813,13 +825,20 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public RValue visitExprId(KickCParser.ExprIdContext ctx) {
Variable variable = getCurrentSymbols().getVariable(ctx.NAME().getText());
if(variable != null) {
Symbol symbol = getCurrentSymbols().getSymbol(ctx.NAME().getText());
if(symbol instanceof Variable) {
Variable variable = (Variable) symbol;
return variable.getRef();
} else {
} else if(symbol instanceof Procedure) {
Procedure procedure = (Procedure) symbol;
if(procedure.isDeclaredInterrupt()) {
return procedure.getRef();
}
} else if(symbol==null){
// Either forward reference or a non-existing variable. Create a forward reference for later resolving.
return new ForwardVariableRef(ctx.NAME().getText());
}
throw new CompileError("Error! Unhandled symbol "+symbol.toString(program));
}
public StatementSequence getSequence() {

View File

@ -6,6 +6,7 @@ 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.types.SymbolType;
import dk.camelot64.kickc.model.values.ProcedureRef;
/** Asserts that interrupts are never called and are not declared inline */
@ -28,8 +29,16 @@ public class Pass1AssertInterrupts extends Pass1Base {
}
}
for(Procedure procedure : getScope().getAllProcedures(true)) {
if(procedure.isDeclaredInline() && procedure.isDeclaredInterrupt()) {
throw new CompileError("Error! Interrupts cannot be inlined. " + procedure.toString());
if(procedure.isDeclaredInterrupt()) {
if(procedure.isDeclaredInline()) {
throw new CompileError("Error! Interrupts cannot be inlined. " + procedure.toString());
}
if(procedure.getParameters().size()>0) {
throw new CompileError("Error! Interrupts cannot have parameters. " + procedure.toString());
}
if(!SymbolType.VOID.equals(procedure.getReturnType())) {
throw new CompileError("Error! Interrupts cannot return anything. " + procedure.toString());
}
}
}
}

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
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;
@ -34,7 +35,7 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base {
Set<ProcedureRef> unusedProcedures = new LinkedHashSet<>();
Collection<Procedure> allProcedures = getProgram().getScope().getAllProcedures(true);
for(Procedure procedure : allProcedures) {
if(!calledProcedures.contains(procedure.getRef())) {
if(!calledProcedures.contains(procedure.getRef()) && !Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), getProgram())) {
// The procedure is not used - mark for removal!
unusedProcedures.add(procedure.getRef());
}

View File

@ -3,7 +3,9 @@ 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.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.operators.OperatorBinary;
import dk.camelot64.kickc.model.operators.OperatorCastPtr;
import dk.camelot64.kickc.model.operators.OperatorUnary;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.Statement;
@ -100,7 +102,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
if(statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
findConstantsAssignment(constants, assignment);
} else if( statement instanceof StatementPhiBlock) {
} else if(statement instanceof StatementPhiBlock) {
StatementPhiBlock phi = (StatementPhiBlock) statement;
findConstantsPhi(constants, phi);
}
@ -131,7 +133,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
if(assignment.getOperator() == null) {
// Constant assignment
ConstantValue constant = getConstant(assignment.getrValue2());
if(constant!=null) {
if(constant != null) {
constants.put(variable, constant);
}
} else {
@ -174,7 +176,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
} else {
if(!SymbolTypeInference.typeMatch(listType, elmType)) {
SymbolType intersectType = SymbolTypeInference.intersectTypes(listType, elmType);
if(intersectType==null) {
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 {
@ -195,11 +197,11 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
constants.put(variable, constant);
}
}
} else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1()==null) {
} else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1() == null) {
// Constant address-of variable
if(assignment.getrValue2() instanceof VariableRef) {
ConstantVarPointer constantVarPointer = new ConstantVarPointer((VariableRef) assignment.getrValue2());
constants.put(variable, constantVarPointer);
if(assignment.getrValue2() instanceof SymbolRef) {
ConstantSymbolPointer constantSymbolPointer = new ConstantSymbolPointer((SymbolRef) assignment.getrValue2());
constants.put(variable, constantSymbolPointer);
}
}
}
@ -220,7 +222,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
} else if(rValue instanceof CastValue) {
CastValue castValue = (CastValue) rValue;
ConstantValue castConstant = getConstant(castValue.getValue());
if(castConstant !=null) {
if(castConstant != null) {
return new ConstantCastValue(castValue.getToType(), castConstant);
}
} else if(rValue instanceof ArrayFilled) {
@ -233,6 +235,9 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
}
static ConstantValue createUnary(OperatorUnary operator, ConstantValue c) {
if(operator instanceof OperatorCastPtr) {
return new ConstantUnary(operator, c);
}
switch(operator.getOperator()) {
case "-":
case "+":
@ -309,37 +314,32 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
/**
* Determines if the variable is ever operated on by the address-of operator
* @param var tHe variable to examine
*
* @param symbolRef tHe variable to examine
* @return true if the address-of operator is used on the variable
*/
public static boolean isAddressOfUsed(VariableRef var, Program program) {
public static boolean isAddressOfUsed(SymbolRef symbolRef, Program program) {
final boolean[] found = {false};
ProgramValueIterator.execute(program, (programValue, currentStmt, stmtIt, currentBlock) -> {
RValue value = programValue.get();
if(value instanceof ConstantSymbolPointer) {
ConstantSymbolPointer constantSymbolPointer = (ConstantSymbolPointer) value;
if(constantSymbolPointer.getToSymbol().equals(symbolRef)) {
found[0] = true;
}
}
});
if(found[0]) {
return true;
}
for(ControlFlowBlock block : program.getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && var.equals(assignment.getrValue2())) {
if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && symbolRef.equals(assignment.getrValue2())) {
return true;
}
} else if(statement instanceof StatementPhiBlock) {
for(StatementPhiBlock.PhiVariable phiVariable : ((StatementPhiBlock) statement).getPhiVariables()) {
for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
if(phiRValue.getrValue() instanceof ConstantVarPointer) {
if(((ConstantVarPointer)phiRValue.getrValue()).getToVar().equals(var)) {
return true;
}
}
}
}
}
}
}
for(ConstantVar constVar : program.getScope().getAllConstants(true)) {
ConstantValue constantValue = constVar.getValue();
if(constantValue instanceof ConstantVarPointer) {
ConstantVarPointer constantVarPointer = (ConstantVarPointer) constantValue;
if(constantVarPointer.getToVar().equals(var)) {
return true;
}
}
}
@ -347,5 +347,4 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
}
}

View File

@ -7,6 +7,7 @@ import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.LabelRef;
@ -26,11 +27,10 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization {
@Override
public boolean step() {
Set<LabelRef> referencedBlocks = new LinkedHashSet<>();
findReferencedBlocks(getGraph().getFirstBlock(), referencedBlocks);
Set<LabelRef> referencedBlocks = getReferencedBlocks();
Set<LabelRef> unusedBlocks = new LinkedHashSet<>();
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
if(!referencedBlocks.contains(block.getLabel())) {
if(!referencedBlocks.contains(block.getLabel()) ) {
unusedBlocks.add(block.getLabel());
for(Statement stmt : block.getStatements()) {
if(stmt instanceof StatementAssignment) {
@ -55,6 +55,24 @@ public class Pass2EliminateUnusedBlocks extends Pass2SsaOptimization {
return unusedBlocks.size()>0;
}
/**
* Get all referenced blocks en the entire program
* @return All blocks referenced
*/
private Set<LabelRef> getReferencedBlocks() {
Set<LabelRef> referencedBlocks = new LinkedHashSet<>();
findReferencedBlocks(getGraph().getFirstBlock(), referencedBlocks);
for(Procedure procedure : getScope().getAllProcedures(true)) {
if(Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), getProgram())) {
// Address-of is used on the procedure
Label procedureLabel = procedure.getLabel();
ControlFlowBlock procedureBlock = getGraph().getBlock(procedureLabel.getRef());
findReferencedBlocks(procedureBlock, referencedBlocks);
}
}
return referencedBlocks;
}
/**
* Remove all PHI RValues in any phi-statements referencing the passed block
* @param removeBlock The block to remove from PHI RValues

View File

@ -1,6 +1,8 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.symbols.Scope;
@ -18,11 +20,23 @@ public class Pass3BlockSequencePlanner extends Pass2Base {
}
public void plan() {
for(Procedure procedure : getProgram().getScope().getAllProcedures(true)) {
if(Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), getProgram())) {
// Address-of is used on the procedure
Label procedureLabel = procedure.getLabel();
ControlFlowBlock procedureBlock = getGraph().getBlock(procedureLabel.getRef());
pushTodo(procedureBlock);
}
}
ControlFlowBlock mainBlock = getGraph().getMainBlock();
if(mainBlock != null) {
pushTodo(mainBlock);
}
pushTodo(getGraph().getFirstBlock());
List<LabelRef> sequence = new ArrayList<>();
while(hasTodo()) {
ControlFlowBlock block = popTodo();

View File

@ -1,6 +1,8 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import java.util.*;
@ -27,7 +29,17 @@ public class Pass3LoopDepthAnalysis extends Pass2Base {
public void findLoopDepths() {
Deque<LabelRef> todo = new ArrayDeque<>();
Set<LabelRef> done = new LinkedHashSet<>();
// Add the main block block
todo.push(callGraph.getFirstCallBlock());
// Also add all address-of referenced blocks
for(Procedure procedure : getProgram().getScope().getAllProcedures(true)) {
if(Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), getProgram())) {
// Address-of is used on the procedure
Label procedureLabel = procedure.getLabel();
todo.push(procedureLabel.getRef());
}
}
while(!todo.isEmpty()) {
LabelRef currentScope = todo.pop();
done.add(currentScope);

View File

@ -5,10 +5,7 @@ import dk.camelot64.kickc.fragment.*;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.ConstantVar;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeArray;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
@ -432,7 +429,20 @@ public class Pass4CodeGeneration {
}
asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else if(statement instanceof StatementReturn) {
asm.addInstruction("rts", AsmAddressingMode.NON, null, false);
boolean isInterrupt = false;
ScopeRef scope = block.getScope();
if(!scope.equals(ScopeRef.ROOT)) {
Procedure procedure = getScope().getProcedure(scope.getFullName());
if(procedure!=null) {
isInterrupt = procedure.isDeclaredInterrupt();
}
}
if(isInterrupt) {
asm.addInstruction("rti", AsmAddressingMode.NON, null, false);
} else {
asm.addInstruction("rts", AsmAddressingMode.NON, null, false);
}
} else if(statement instanceof StatementAsm) {
StatementAsm statementAsm = (StatementAsm) statement;
HashMap<String, Value> bindings = new HashMap<>();

View File

@ -1,7 +1,8 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.values.ConstantVarPointer;
import dk.camelot64.kickc.model.values.ConstantSymbolPointer;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.symbols.ConstantVar;
import dk.camelot64.kickc.model.symbols.Variable;
@ -70,9 +71,9 @@ public class Pass4RegisterUpliftPotentialInitialize extends Pass2Base {
private boolean varRefExtracted(LiveRangeEquivalenceClass equivalenceClass) {
Collection<ConstantVar> allConstants = getProgram().getScope().getAllConstants(true);
for(ConstantVar allConstant : allConstants) {
if(allConstant.getValue() instanceof ConstantVarPointer) {
VariableRef toVar = ((ConstantVarPointer) allConstant.getValue()).getToVar();
if(equivalenceClass.getVariables().contains(toVar)) {
if(allConstant.getValue() instanceof ConstantSymbolPointer) {
SymbolRef toSym = ((ConstantSymbolPointer) allConstant.getValue()).getToSymbol();
if(equivalenceClass.getVariables().contains(toSym)) {
return true;
}
}

View File

@ -46,6 +46,11 @@ public class TestPrograms {
AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false);
}
@Test
public void testInterrupt() throws IOException, URISyntaxException {
compileAndCompare("test-interrupt");
}
@Test
public void testMultiplexer() throws IOException, URISyntaxException {
compileAndCompare("simple-multiplexer");
@ -891,6 +896,16 @@ public class TestPrograms {
assertError("no-calledinterrupt", "Interrupts cannot be called.");
}
@Test
public void testNoParamInterrupt() throws IOException, URISyntaxException {
assertError("no-paraminterrupt", "Interrupts cannot have parameters.");
}
@Test
public void testNoReturnInterrupt() throws IOException, URISyntaxException {
assertError("no-returninterrupt", "Interrupts cannot return anything.");
}
private void assertError(String kcFile, String expectError) throws IOException, URISyntaxException {
try {
compileAndCompare(kcFile);

View File

@ -0,0 +1,11 @@
// Test that inline interrupts not allowed
byte* SCREEN = $400;
void main() {
SCREEN[0]++;
}
interrupt void irq(byte b) {
SCREEN[1]++;
}

View File

@ -0,0 +1,12 @@
// Test that inline interrupts not allowed
byte* SCREEN = $400;
void main() {
SCREEN[0]++;
}
interrupt byte irq() {
SCREEN[1]++;
return 2;
}

View File

@ -0,0 +1,22 @@
void()** VECTOR_IRQ = $0314;
interrupt void irq() {
byte* BGCOL = $d020;
(*BGCOL)++;
asm {
lda $dc0d
pla
tay
pla
tax
pla
}
}
void main() {
*VECTOR_IRQ = &irq;
byte* FGCOL = $d021;
while(true) {
(*FGCOL)++;
}
}

View File

@ -0,0 +1,26 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label VECTOR_IRQ = $314
jsr main
main: {
.label FGCOL = $d021
lda #<irq
sta VECTOR_IRQ
lda #>irq
sta VECTOR_IRQ+1
b2:
inc FGCOL
jmp b2
}
irq: {
.label BGCOL = $d020
inc BGCOL
lda $dc0d
pla
tay
pla
tax
pla
rti
}

View File

@ -0,0 +1,22 @@
@begin: scope:[] from
[0] phi() [ ] ( )
to:@2
@2: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main [ ] ( )
to:@end
@end: scope:[] from @2
[3] phi() [ ] ( )
main: scope:[main] from @2
[4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] )
to:main::@2
main::@2: scope:[main] from main main::@2
[5] *((const byte*) main::FGCOL#0) ← ++ *((const byte*) main::FGCOL#0) [ ] ( main:2 [ ] )
to:main::@2
irq: scope:[irq] from
[6] *((const byte*) irq::BGCOL#0) ← ++ *((const byte*) irq::BGCOL#0) [ ] ( )
asm { lda$dc0d pla tay pla tax pla }
to:irq::@return
irq::@return: scope:[irq] from irq
[8] return [ ] ( )
to:@return

View File

@ -0,0 +1,467 @@
PARSING src/test/java/dk/camelot64/kickc/test/kc/test-interrupt.kc
void()** VECTOR_IRQ = $0314;
interrupt void irq() {
byte* BGCOL = $d020;
(*BGCOL)++;
asm {
lda $dc0d
pla
tay
pla
tax
pla
}
}
void main() {
*VECTOR_IRQ = &irq;
byte* FGCOL = $d021;
while(true) {
(*FGCOL)++;
}
}
Adding pre/post-modifier *((byte*) irq::BGCOL) ← ++ *((byte*) irq::BGCOL)
Adding pre/post-modifier *((byte*) main::FGCOL) ← ++ *((byte*) main::FGCOL)
SYMBOLS
(label) @1
(label) @2
(label) @begin
(label) @end
(void()**) VECTOR_IRQ
interrupt (void()) irq()
(label) irq::@return
(byte*) irq::BGCOL
(void()) main()
(void()*~) main::$0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@5
(label) main::@6
(label) main::@return
(byte*) main::FGCOL
Promoting word/signed word/dword/signed dword to void()** in VECTOR_IRQ ← ((void()**)) 788
Promoting word/dword/signed dword to byte* in irq::BGCOL ← ((byte*)) 53280
Promoting word/dword/signed dword to byte* in main::FGCOL ← ((byte*)) 53281
INITIAL CONTROL FLOW GRAPH
@begin: scope:[] from
(void()**) VECTOR_IRQ ← ((void()**)) (word/signed word/dword/signed dword) 788
to:@1
irq: scope:[irq] from
(byte*) irq::BGCOL ← ((byte*)) (word/dword/signed dword) 53280
*((byte*) irq::BGCOL) ← ++ *((byte*) irq::BGCOL)
asm { lda$dc0d pla tay pla tax pla }
to:irq::@return
irq::@return: scope:[irq] from irq
return
to:@return
@1: scope:[] from @begin
to:@2
main: scope:[main] from
(void()*~) main::$0 ← & interrupt (void()) irq()
*((void()**) VECTOR_IRQ) ← (void()*~) main::$0
(byte*) main::FGCOL ← ((byte*)) (word/dword/signed dword) 53281
to:main::@1
main::@1: scope:[main] from main main::@2
if(true) goto main::@2
to:main::@4
main::@2: scope:[main] from main::@1 main::@5
*((byte*) main::FGCOL) ← ++ *((byte*) main::FGCOL)
to:main::@1
main::@4: scope:[main] from main::@1
to:main::@3
main::@3: scope:[main] from main::@4 main::@6
to:main::@return
main::@5: scope:[main] from
to:main::@2
main::@6: scope:[main] from
to:main::@3
main::@return: scope:[main] from main::@3
return
to:@return
@2: scope:[] from @1
call main
to:@end
@end: scope:[] from @2
Removing empty block @1
Removing empty block main::@4
Removing empty block main::@3
Removing empty block main::@5
Removing empty block main::@6
PROCEDURE MODIFY VARIABLE ANALYSIS
Completing Phi functions...
Completing Phi functions...
CONTROL FLOW GRAPH SSA WITH ASSIGNMENT CALL & RETURN
@begin: scope:[] from
(void()**) VECTOR_IRQ#0 ← ((void()**)) (word/signed word/dword/signed dword) 788
to:@2
irq: scope:[irq] from
(byte*) irq::BGCOL#0 ← ((byte*)) (word/dword/signed dword) 53280
*((byte*) irq::BGCOL#0) ← ++ *((byte*) irq::BGCOL#0)
asm { lda$dc0d pla tay pla tax pla }
to:irq::@return
irq::@return: scope:[irq] from irq
return
to:@return
main: scope:[main] from @2
(void()**) VECTOR_IRQ#1 ← phi( @2/(void()**) VECTOR_IRQ#2 )
(void()*~) main::$0 ← & interrupt (void()) irq()
*((void()**) VECTOR_IRQ#1) ← (void()*~) main::$0
(byte*) main::FGCOL#0 ← ((byte*)) (word/dword/signed dword) 53281
to:main::@1
main::@1: scope:[main] from main main::@2
(byte*) main::FGCOL#2 ← phi( main/(byte*) main::FGCOL#0 main::@2/(byte*) main::FGCOL#1 )
if(true) goto main::@2
to:main::@return
main::@2: scope:[main] from main::@1
(byte*) main::FGCOL#1 ← phi( main::@1/(byte*) main::FGCOL#2 )
*((byte*) main::FGCOL#1) ← ++ *((byte*) main::FGCOL#1)
to:main::@1
main::@return: scope:[main] from main::@1
return
to:@return
@2: scope:[] from @begin
(void()**) VECTOR_IRQ#2 ← phi( @begin/(void()**) VECTOR_IRQ#0 )
call main
to:@3
@3: scope:[] from @2
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @2
(label) @3
(label) @begin
(label) @end
(void()**) VECTOR_IRQ
(void()**) VECTOR_IRQ#0
(void()**) VECTOR_IRQ#1
(void()**) VECTOR_IRQ#2
interrupt (void()) irq()
(label) irq::@return
(byte*) irq::BGCOL
(byte*) irq::BGCOL#0
(void()) main()
(void()*~) main::$0
(label) main::@1
(label) main::@2
(label) main::@return
(byte*) main::FGCOL
(byte*) main::FGCOL#0
(byte*) main::FGCOL#1
(byte*) main::FGCOL#2
OPTIMIZING CONTROL FLOW GRAPH
Culled Empty Block (label) @3
Succesful SSA optimization Pass2CullEmptyBlocks
Not aliassing across scopes: VECTOR_IRQ#1 VECTOR_IRQ#2
Alias (byte*) main::FGCOL#1 = (byte*) main::FGCOL#2
Alias (void()**) VECTOR_IRQ#0 = (void()**) VECTOR_IRQ#2
Succesful SSA optimization Pass2AliasElimination
Not aliassing across scopes: VECTOR_IRQ#1 VECTOR_IRQ#0
Self Phi Eliminated (byte*) main::FGCOL#1
Succesful SSA optimization Pass2SelfPhiElimination
Redundant Phi (void()**) VECTOR_IRQ#1 (void()**) VECTOR_IRQ#0
Redundant Phi (byte*) main::FGCOL#1 (byte*) main::FGCOL#0
Succesful SSA optimization Pass2RedundantPhiElimination
Constant (const void()**) VECTOR_IRQ#0 = ((void()**))788
Constant (const byte*) irq::BGCOL#0 = ((byte*))53280
Constant (const void()*) main::$0 = &irq
Constant (const byte*) main::FGCOL#0 = ((byte*))53281
Succesful SSA optimization Pass2ConstantIdentification
if() condition always true - replacing block destination if(true) goto main::@2
Succesful SSA optimization Pass2ConstantIfs
Removing unused block main::@return
Succesful SSA optimization Pass2EliminateUnusedBlocks
Culled Empty Block (label) main::@1
Succesful SSA optimization Pass2CullEmptyBlocks
OPTIMIZING CONTROL FLOW GRAPH
Constant inlined main::$0 = &interrupt (void()) irq()
Succesful SSA optimization Pass2ConstantInlining
Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return
Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Propagating live ranges...
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Block Sequence Planned @begin @2 @end main main::@2 irq irq::@return
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
Propagating live ranges...
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi() [ ] ( )
to:@2
@2: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main [ ] ( )
to:@end
@end: scope:[] from @2
[3] phi() [ ] ( )
main: scope:[main] from @2
[4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] )
to:main::@2
main::@2: scope:[main] from main main::@2
[5] *((const byte*) main::FGCOL#0) ← ++ *((const byte*) main::FGCOL#0) [ ] ( main:2 [ ] )
to:main::@2
irq: scope:[irq] from
[6] *((const byte*) irq::BGCOL#0) ← ++ *((const byte*) irq::BGCOL#0) [ ] ( )
asm { lda$dc0d pla tay pla tax pla }
to:irq::@return
irq::@return: scope:[irq] from irq
[8] return [ ] ( )
to:@return
DOMINATORS
@begin dominated by @begin
@2 dominated by @2 @begin
@end dominated by @2 @begin @end
main dominated by @2 @begin main
main::@2 dominated by @2 @begin main::@2 main
irq dominated by @2 @begin @end main::@2 irq irq::@return main
irq::@return dominated by @2 @begin @end main::@2 irq irq::@return main
NATURAL LOOPS
Found back edge: Loop head: main::@2 tails: main::@2 blocks: null
Found back edge: Loop head: irq::@return tails: irq blocks: null
Populated: Loop head: main::@2 tails: main::@2 blocks: main::@2
Populated: Loop head: irq::@return tails: irq blocks: irq
Loop head: main::@2 tails: main::@2 blocks: main::@2
Loop head: irq::@return tails: irq blocks: irq
NATURAL LOOPS WITH DEPTH
Found 1 loops in scope [irq]
Loop head: irq::@return tails: irq blocks: irq
Found 0 loops in scope []
Found 1 loops in scope [main]
Loop head: main::@2 tails: main::@2 blocks: main::@2
Loop head: main::@2 tails: main::@2 blocks: main::@2 depth: 1
Loop head: irq::@return tails: irq blocks: irq depth: 1
VARIABLE REGISTER WEIGHTS
(void()**) VECTOR_IRQ
interrupt (void()) irq()
(byte*) irq::BGCOL
(void()) main()
(byte*) main::FGCOL
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.label VECTOR_IRQ = $314
//SEG2 @begin
bbegin:
//SEG3 [1] phi from @begin to @2 [phi:@begin->@2]
b2_from_bbegin:
jmp b2
//SEG4 @2
b2:
//SEG5 [2] call main [ ] ( )
jsr main
//SEG6 [3] phi from @2 to @end [phi:@2->@end]
bend_from_b2:
jmp bend
//SEG7 @end
bend:
//SEG8 main
main: {
.label FGCOL = $d021
//SEG9 [4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2
lda #<irq
sta VECTOR_IRQ
lda #>irq
sta VECTOR_IRQ+1
jmp b2
//SEG10 main::@2
b2:
//SEG11 [5] *((const byte*) main::FGCOL#0) ← ++ *((const byte*) main::FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1
inc FGCOL
jmp b2
}
//SEG12 irq
irq: {
.label BGCOL = $d020
//SEG13 [6] *((const byte*) irq::BGCOL#0) ← ++ *((const byte*) irq::BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1
inc BGCOL
//SEG14 asm { lda$dc0d pla tay pla tax pla }
lda $dc0d
pla
tay
pla
tax
pla
jmp breturn
//SEG15 irq::@return
breturn:
//SEG16 [8] return [ ] ( )
rti
}
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement asm { lda$dc0d pla tay pla tax pla } always clobbers reg byte a reg byte x reg byte y
REGISTER UPLIFT SCOPES
Uplift Scope [irq]
Uplift Scope [main]
Uplift Scope []
Uplifting [irq] best 413 combination
Uplifting [main] best 413 combination
Uplifting [] best 413 combination
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.label VECTOR_IRQ = $314
//SEG2 @begin
bbegin:
//SEG3 [1] phi from @begin to @2 [phi:@begin->@2]
b2_from_bbegin:
jmp b2
//SEG4 @2
b2:
//SEG5 [2] call main [ ] ( )
jsr main
//SEG6 [3] phi from @2 to @end [phi:@2->@end]
bend_from_b2:
jmp bend
//SEG7 @end
bend:
//SEG8 main
main: {
.label FGCOL = $d021
//SEG9 [4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2
lda #<irq
sta VECTOR_IRQ
lda #>irq
sta VECTOR_IRQ+1
jmp b2
//SEG10 main::@2
b2:
//SEG11 [5] *((const byte*) main::FGCOL#0) ← ++ *((const byte*) main::FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1
inc FGCOL
jmp b2
}
//SEG12 irq
irq: {
.label BGCOL = $d020
//SEG13 [6] *((const byte*) irq::BGCOL#0) ← ++ *((const byte*) irq::BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1
inc BGCOL
//SEG14 asm { lda$dc0d pla tay pla tax pla }
lda $dc0d
pla
tay
pla
tax
pla
jmp breturn
//SEG15 irq::@return
breturn:
//SEG16 [8] return [ ] ( )
rti
}
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b2
Removing instruction jmp bend
Removing instruction jmp b2
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction bbegin:
Removing instruction b2_from_bbegin:
Removing instruction bend_from_b2:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction b2:
Removing instruction bend:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @2
(label) @begin
(label) @end
(void()**) VECTOR_IRQ
(const void()**) VECTOR_IRQ#0 VECTOR_IRQ = ((void()**))(word/signed word/dword/signed dword) 788
interrupt (void()) irq()
(label) irq::@return
(byte*) irq::BGCOL
(const byte*) irq::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) 53280
(void()) main()
(label) main::@2
(byte*) main::FGCOL
(const byte*) main::FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281
FINAL ASSEMBLER
Score: 374
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.label VECTOR_IRQ = $314
//SEG2 @begin
//SEG3 [1] phi from @begin to @2 [phi:@begin->@2]
//SEG4 @2
//SEG5 [2] call main [ ] ( )
jsr main
//SEG6 [3] phi from @2 to @end [phi:@2->@end]
//SEG7 @end
//SEG8 main
main: {
.label FGCOL = $d021
//SEG9 [4] *((const void()**) VECTOR_IRQ#0) ← &interrupt (void()) irq() [ ] ( main:2 [ ] ) -- _deref_pptc1=pprc2
lda #<irq
sta VECTOR_IRQ
lda #>irq
sta VECTOR_IRQ+1
//SEG10 main::@2
b2:
//SEG11 [5] *((const byte*) main::FGCOL#0) ← ++ *((const byte*) main::FGCOL#0) [ ] ( main:2 [ ] ) -- _deref_pbuc1=_inc__deref_pbuc1
inc FGCOL
jmp b2
}
//SEG12 irq
irq: {
.label BGCOL = $d020
//SEG13 [6] *((const byte*) irq::BGCOL#0) ← ++ *((const byte*) irq::BGCOL#0) [ ] ( ) -- _deref_pbuc1=_inc__deref_pbuc1
inc BGCOL
//SEG14 asm { lda$dc0d pla tay pla tax pla }
lda $dc0d
pla
tay
pla
tax
pla
//SEG15 irq::@return
//SEG16 [8] return [ ] ( )
rti
}

View File

@ -0,0 +1,14 @@
(label) @2
(label) @begin
(label) @end
(void()**) VECTOR_IRQ
(const void()**) VECTOR_IRQ#0 VECTOR_IRQ = ((void()**))(word/signed word/dword/signed dword) 788
interrupt (void()) irq()
(label) irq::@return
(byte*) irq::BGCOL
(const byte*) irq::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) 53280
(void()) main()
(label) main::@2
(byte*) main::FGCOL
(const byte*) main::FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) 53281