mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-11 16:30:56 +00:00
Snapshotting program dynamic state now working through Serialization. Working on #246
This commit is contained in:
parent
aceea6d8ec
commit
198280e1c8
34
src/main/java/dk/camelot64/kickc/AsmParser.java
Normal file
34
src/main/java/dk/camelot64/kickc/AsmParser.java
Normal file
@ -0,0 +1,34 @@
|
||||
package dk.camelot64.kickc;
|
||||
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.statements.StatementSource;
|
||||
import dk.camelot64.kickc.parser.KickCLexer;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import org.antlr.v4.runtime.*;
|
||||
|
||||
public class AsmParser {
|
||||
|
||||
/**
|
||||
* Parse assembler code
|
||||
* @param body The assembler to parse
|
||||
* @param source Information about the source of the ASM
|
||||
* @return The parser assembler
|
||||
*/
|
||||
public static KickCParser.AsmLinesContext parseAsm(String body, StatementSource source) {
|
||||
CodePointCharStream fragmentCharStream = CharStreams.fromString(body);
|
||||
KickCLexer kickCLexer = new KickCLexer(fragmentCharStream);
|
||||
KickCParser kickCParser = new KickCParser(new CommonTokenStream(kickCLexer));
|
||||
kickCParser.addErrorListener(new BaseErrorListener() {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
|
||||
StatementSource subSource =
|
||||
new StatementSource(source.getFileName(), source.getLineNumber() + line, source.getCode(), source.getStartIndex(), source.getStopIndex());
|
||||
|
||||
throw new CompileError("Error parsing assembler. " + msg, subSource);
|
||||
}
|
||||
});
|
||||
kickCParser.setBuildParseTree(true);
|
||||
return kickCParser.asmFile().asmLines();
|
||||
}
|
||||
|
||||
}
|
@ -262,6 +262,7 @@ public class KickC implements Callable<Void> {
|
||||
System.setOut(new PrintStream(kasmLogOutputStream));
|
||||
int kasmResult = -1;
|
||||
try {
|
||||
//CharToPetsciiConverter.setCurrentEncoding("screencode_mixed");
|
||||
kasmResult = KickAssembler.main2(assembleCommand);
|
||||
} catch(Throwable e) {
|
||||
throw new CompileError("KickAssembling file failed! ", e);
|
||||
|
@ -1,7 +1,9 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** Information about what parts of the CPU an ASM instruction clobbers */
|
||||
public class AsmClobber {
|
||||
public class AsmClobber implements Serializable {
|
||||
|
||||
public static final AsmClobber CLOBBER_ALL = new AsmClobber(true);
|
||||
boolean clobberA;
|
||||
|
@ -205,10 +205,10 @@ public class AsmSegment {
|
||||
if(statement != null) {
|
||||
StatementSource source = statement.getSource();
|
||||
if(source != null) {
|
||||
if(source.getFile() != null || source.getLineNumber() != null) {
|
||||
if(source.getFileName() != null || source.getLineNumber() != null) {
|
||||
String fileInfo = "";
|
||||
if(source.getFile() != null)
|
||||
fileInfo += source.getFile();
|
||||
if(source.getFileName() != null)
|
||||
fileInfo += source.getFileName();
|
||||
fileInfo += ":";
|
||||
if(source.getLineNumber() != null)
|
||||
fileInfo += source.getLineNumber();
|
||||
|
@ -1,18 +1,19 @@
|
||||
package dk.camelot64.kickc.fragment;
|
||||
|
||||
import dk.camelot64.kickc.AsmParser;
|
||||
import dk.camelot64.kickc.asm.AsmClobber;
|
||||
import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.model.*;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.Registers;
|
||||
import dk.camelot64.kickc.model.statements.StatementSource;
|
||||
import dk.camelot64.kickc.model.symbols.Label;
|
||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.symbols.VariableVersion;
|
||||
import dk.camelot64.kickc.model.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ScopeRef;
|
||||
import dk.camelot64.kickc.model.values.Value;
|
||||
import dk.camelot64.kickc.parser.KickCLexer;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import org.antlr.v4.runtime.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@ -75,17 +76,7 @@ public class AsmFragmentTemplate {
|
||||
*/
|
||||
private void initAsm() {
|
||||
// Parse the body ASM
|
||||
CodePointCharStream fragmentCharStream = CharStreams.fromString(body);
|
||||
KickCLexer kickCLexer = new KickCLexer(fragmentCharStream);
|
||||
KickCParser kickCParser = new KickCParser(new CommonTokenStream(kickCLexer));
|
||||
kickCParser.addErrorListener(new BaseErrorListener() {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
|
||||
throw new RuntimeException("Error parsing fragment " + signature + "\n - Line: " + line + "\n - Message: " + msg);
|
||||
}
|
||||
});
|
||||
kickCParser.setBuildParseTree(true);
|
||||
this.bodyAsm = kickCParser.asmFile().asmLines();
|
||||
this.bodyAsm = AsmParser.parseAsm(this.body, new StatementSource(signature+".asm", 1, this.body, 0, 0));
|
||||
// Generate a dummy instance to find clobber & cycles
|
||||
ProgramScope scope = new ProgramScope();
|
||||
LinkedHashMap<String, Value> bindings = new LinkedHashMap<>();
|
||||
@ -123,7 +114,7 @@ public class AsmFragmentTemplate {
|
||||
AsmFragmentInstance fragmentInstance =
|
||||
new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings);
|
||||
AsmProgram asm = new AsmProgram();
|
||||
asm.startSegment( ScopeRef.ROOT, null, signature);
|
||||
asm.startSegment(ScopeRef.ROOT, null, signature);
|
||||
fragmentInstance.generate(asm);
|
||||
AsmClobber asmClobber = asm.getClobber();
|
||||
this.clobber = new AsmFragmentClobber(asmClobber);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dk.camelot64.kickc.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@ -7,7 +8,7 @@ import java.util.ArrayList;
|
||||
* Comments are attached to symbols and statements and
|
||||
* can be output in the generated assembler code.
|
||||
**/
|
||||
public class Comment {
|
||||
public class Comment implements Serializable {
|
||||
|
||||
/** Empty comments collection. */
|
||||
public static final ArrayList<Comment> NO_COMMENTS = new ArrayList<>();
|
||||
|
@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.symbols.Symbol;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.ScopeRef;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -18,7 +19,7 @@ import java.util.ListIterator;
|
||||
* The connections defines the control flow of the program.
|
||||
* The block only knows its own successors. To find predecessor blocks access to the entire graph is needed.
|
||||
*/
|
||||
public class ControlFlowBlock {
|
||||
public class ControlFlowBlock implements Serializable {
|
||||
|
||||
/** The label representing the block. */
|
||||
private LabelRef label;
|
||||
|
@ -12,13 +12,14 @@ import dk.camelot64.kickc.model.values.SymbolRef;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
import dk.camelot64.kickc.passes.Pass2ConstantIdentification;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The control flow graph of the program.
|
||||
* The control flow graph is a set of connected basic blocks.
|
||||
*/
|
||||
public class ControlFlowGraph {
|
||||
public class ControlFlowGraph implements Serializable {
|
||||
|
||||
private List<ControlFlowBlock> blocks;
|
||||
private LabelRef firstBlockRef;
|
||||
|
@ -198,7 +198,7 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
|
||||
|
||||
@Override
|
||||
public Object visitAsm(StatementAsm orig) {
|
||||
return new StatementAsm(orig.getAsmLines(), orig.getReferenced(), orig.getDeclaredClobber(), orig.getSource(), orig.getComments());
|
||||
return new StatementAsm(orig.getAsmBody(), orig.getReferenced(), orig.getDeclaredClobber(), orig.getSource(), orig.getComments());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,9 +1,11 @@
|
||||
package dk.camelot64.kickc.model.operators;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An Operator. The operation performed on the rvalues in a Statement.
|
||||
*/
|
||||
public class Operator {
|
||||
public class Operator implements Serializable {
|
||||
|
||||
private String operator;
|
||||
private int precedence;
|
||||
|
@ -4,13 +4,14 @@ package dk.camelot64.kickc.model.statements;
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Single Static Assignment Form Statement.
|
||||
* Intermediate form used for compiler optimization.
|
||||
*/
|
||||
public interface Statement {
|
||||
public interface Statement extends Serializable {
|
||||
|
||||
String toString(Program program, boolean aliveInfo);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dk.camelot64.kickc.model.statements;
|
||||
|
||||
import dk.camelot64.kickc.AsmParser;
|
||||
import dk.camelot64.kickc.asm.AsmClobber;
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
@ -12,8 +13,11 @@ import java.util.Map;
|
||||
/** Inline ASM code */
|
||||
public class StatementAsm extends StatementBase {
|
||||
|
||||
/** ASM Fragment code. */
|
||||
private KickCParser.AsmLinesContext asmLines;
|
||||
/** ASM code. */
|
||||
private String asmBody;
|
||||
|
||||
/** Cached parsed ASM code. */
|
||||
private transient KickCParser.AsmLinesContext asmLines;
|
||||
|
||||
/** All variables/constants referenced in the inline assembler. */
|
||||
private Map<String, SymbolVariableRef> referenced;
|
||||
@ -21,9 +25,9 @@ public class StatementAsm extends StatementBase {
|
||||
/** Declared clobber for the inline ASM. */
|
||||
private AsmClobber declaredClobber;
|
||||
|
||||
public StatementAsm(KickCParser.AsmLinesContext asmLines, Map<String, SymbolVariableRef> referenced, AsmClobber declaredClobber, StatementSource source, List<Comment> comments) {
|
||||
public StatementAsm(String asmBody, Map<String, SymbolVariableRef> referenced, AsmClobber declaredClobber, StatementSource source, List<Comment> comments) {
|
||||
super(null, source, comments);
|
||||
this.asmLines = asmLines;
|
||||
this.asmBody = asmBody;
|
||||
this.referenced = referenced;
|
||||
this.declaredClobber = declaredClobber;
|
||||
}
|
||||
@ -32,14 +36,21 @@ public class StatementAsm extends StatementBase {
|
||||
public String toString(Program program, boolean aliveInfo) {
|
||||
StringBuilder txt = new StringBuilder();
|
||||
txt.append("asm { ");
|
||||
for(KickCParser.AsmLineContext line : asmLines.asmLine()) {
|
||||
for(KickCParser.AsmLineContext line : getAsmLines().asmLine()) {
|
||||
txt.append(line.getText()).append(" ");
|
||||
}
|
||||
txt.append(" }");
|
||||
return txt.toString();
|
||||
}
|
||||
|
||||
public String getAsmBody() {
|
||||
return asmBody;
|
||||
}
|
||||
|
||||
public KickCParser.AsmLinesContext getAsmLines() {
|
||||
if(asmLines==null) {
|
||||
this.asmLines = AsmParser.parseAsm(asmBody, getSource());
|
||||
}
|
||||
return asmLines;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
import org.antlr.v4.runtime.RuleContext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -122,7 +123,7 @@ public class StatementPhiBlock extends StatementBase {
|
||||
/**
|
||||
* A variable being defined as part of a phi predecessor.
|
||||
*/
|
||||
public static class PhiVariable {
|
||||
public static class PhiVariable implements Serializable {
|
||||
|
||||
/**
|
||||
* The variable being defined.
|
||||
@ -219,7 +220,7 @@ public class StatementPhiBlock extends StatementBase {
|
||||
/**
|
||||
* The value assigned to a phi variable when entering the predecessor from a specific predecessor block.
|
||||
*/
|
||||
public static class PhiRValue {
|
||||
public static class PhiRValue implements Serializable {
|
||||
|
||||
/**
|
||||
* The predecessor predecessor
|
||||
|
@ -9,26 +9,55 @@ import org.antlr.v4.runtime.misc.Interval;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** Contains information about the source of a program statement */
|
||||
public class StatementSource {
|
||||
public class StatementSource implements Serializable {
|
||||
|
||||
private Token tokenStart;
|
||||
/** The file name of the file containing the source. */
|
||||
private String fileName;
|
||||
/** The line number of the start of the source. */
|
||||
private Integer lineNumber;
|
||||
/** The source code. */
|
||||
private String code;
|
||||
/** The index of the first char of the source in the file. */
|
||||
private int startIndex;
|
||||
/** The index of the last char of the source in the file. */
|
||||
private int stopIndex;
|
||||
|
||||
private Token tokenStop;
|
||||
public StatementSource(String fileName, Integer lineNumber, String code, int startIndex, int stopIndex) {
|
||||
this.fileName = fileName;
|
||||
this.lineNumber = lineNumber;
|
||||
this.code = code;
|
||||
this.startIndex = startIndex;
|
||||
this.stopIndex = stopIndex;
|
||||
}
|
||||
|
||||
public StatementSource(Token tokenStart, Token tokenStop) {
|
||||
if(tokenStart != null) {
|
||||
this.startIndex = tokenStart.getStartIndex();
|
||||
CharStream stream = tokenStart.getInputStream();
|
||||
this.fileName = stream.getSourceName();
|
||||
this.lineNumber = tokenStart.getLine();
|
||||
if(tokenStop != null) {
|
||||
this.stopIndex = tokenStop.getStopIndex();
|
||||
Interval interval = getInterval();
|
||||
this.code = stream.getText(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StatementSource(ParserRuleContext context) {
|
||||
this.tokenStart = context.getStart();
|
||||
this.tokenStop = context.getStop();
|
||||
this(context.getStart(), context.getStop());
|
||||
}
|
||||
|
||||
public StatementSource(ParseTree start, ParseTree stop) {
|
||||
this.tokenStart = getToken(start, true);
|
||||
this.tokenStop = getToken(stop, false);
|
||||
this(getToken(start, true), getToken(stop, false));
|
||||
}
|
||||
|
||||
public static Token getToken(ParseTree node, boolean start) {
|
||||
Token token;
|
||||
if(node==null) {
|
||||
if(node == null) {
|
||||
return null;
|
||||
} else if(node instanceof TerminalNode) {
|
||||
token = ((TerminalNode) node).getSymbol();
|
||||
@ -58,7 +87,7 @@ public class StatementSource {
|
||||
|
||||
public static StatementSource ifThen(KickCParser.StmtIfElseContext ctx) {
|
||||
ParseTree nodeStart = ctx;
|
||||
if(ctx.stmt(1)==null) {
|
||||
if(ctx.stmt(1) == null) {
|
||||
// No else part
|
||||
ParseTree nodeStop = ctx.getChild(ctx.getChildCount() - 2);
|
||||
return new StatementSource(nodeStart, nodeStop);
|
||||
@ -88,14 +117,14 @@ public class StatementSource {
|
||||
}
|
||||
|
||||
public static StatementSource procedureEnd(KickCParser.DeclFunctionContext ctx) {
|
||||
ParseTree nodeStart = ctx.getChild(ctx.getChildCount()-1);
|
||||
ParseTree nodeStart = ctx.getChild(ctx.getChildCount() - 1);
|
||||
ParseTree nodeStop = ctx;
|
||||
return new StatementSource(nodeStart, nodeStop);
|
||||
}
|
||||
|
||||
public static StatementSource procedureBegin(KickCParser.DeclFunctionContext ctx) {
|
||||
ParseTree nodeStart = ctx;
|
||||
ParseTree nodeStop = ctx.getChild(ctx.getChildCount()-4);
|
||||
ParseTree nodeStop = ctx.getChild(ctx.getChildCount() - 4);
|
||||
return new StatementSource(nodeStart, nodeStop);
|
||||
}
|
||||
|
||||
@ -113,55 +142,49 @@ public class StatementSource {
|
||||
|
||||
/**
|
||||
* Get the underlying source token interval
|
||||
*
|
||||
* @return The interval
|
||||
*/
|
||||
private Interval getInterval() {
|
||||
return new Interval(tokenStart.getStartIndex(), tokenStop.getStopIndex());
|
||||
return new Interval(startIndex, stopIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this source contains another source
|
||||
*
|
||||
* @param other The other source to examine
|
||||
* @return true if this source contains the other source
|
||||
*/
|
||||
public boolean contains(StatementSource other) {
|
||||
if(other==null)
|
||||
if(other == null)
|
||||
return false;
|
||||
return getInterval().properlyContains(other.getInterval());
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
if(tokenStart != null) {
|
||||
CharStream stream = tokenStart.getInputStream();
|
||||
return stream.getSourceName();
|
||||
}
|
||||
return null;
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public Integer getLineNumber() {
|
||||
if(tokenStart != null) {
|
||||
return tokenStart.getLine();
|
||||
}
|
||||
return null;
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
if(tokenStart != null) {
|
||||
CharStream stream = tokenStart.getInputStream();
|
||||
Interval interval = getInterval();
|
||||
String codeText = stream.getText(interval);
|
||||
return codeText;
|
||||
}
|
||||
return null;
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getStartIndex() {
|
||||
return startIndex;
|
||||
}
|
||||
|
||||
public int getStopIndex() {
|
||||
return stopIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if(tokenStart != null) {
|
||||
CharStream stream = tokenStart.getInputStream();
|
||||
Interval interval = getInterval();
|
||||
return "File " + stream.getSourceName() + "\nLine " + tokenStart.getLine() + "\n" + stream.getText(interval);
|
||||
if(getFileName() != null) {
|
||||
return "File " + getFileName() + "\nLine " + getLineNumber() + "\n" + getCode();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -7,12 +7,13 @@ import dk.camelot64.kickc.model.VariableRegisterWeights;
|
||||
import dk.camelot64.kickc.model.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Manages symbols (variables, labels)
|
||||
*/
|
||||
public abstract class Scope implements Symbol {
|
||||
public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
private String name;
|
||||
private HashMap<String, Symbol> symbols;
|
||||
|
@ -1,7 +1,9 @@
|
||||
package dk.camelot64.kickc.model.types;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** Symbol Types */
|
||||
public interface SymbolType {
|
||||
public interface SymbolType extends Serializable {
|
||||
|
||||
/** Unsigned byte (8 bits)). */
|
||||
SymbolTypeIntegerFixed BYTE = new SymbolTypeIntegerFixed("byte", 0, 255, false, 8);
|
||||
|
@ -2,6 +2,7 @@ package dk.camelot64.kickc.model.types;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Integer type with a fixed size (byte, signed byte, word, ...). */
|
||||
public class SymbolTypeIntegerFixed implements SymbolTypeInteger {
|
||||
@ -102,4 +103,17 @@ public class SymbolTypeIntegerFixed implements SymbolTypeInteger {
|
||||
public String toString() {
|
||||
return getTypeName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
SymbolTypeIntegerFixed that = (SymbolTypeIntegerFixed) o;
|
||||
return Objects.equals(typeName, that.typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(typeName);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package dk.camelot64.kickc.model.values;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** Any value (variable, constant, label) */
|
||||
public interface Value {
|
||||
public interface Value extends Serializable {
|
||||
String toString(Program program);
|
||||
}
|
||||
|
@ -11,9 +11,11 @@ import dk.camelot64.kickc.model.types.*;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
import dk.camelot64.kickc.parser.KickCBaseVisitor;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.misc.Interval;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
@ -1118,11 +1120,27 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementAsm statementAsm = new StatementAsm(ctx.asmLines(), referenced, declaredClobber, StatementSource.asm(ctx), comments);
|
||||
StatementAsm statementAsm = new StatementAsm(getSourceBody(ctx.asmLines()), referenced, declaredClobber, StatementSource.asm(ctx), comments);
|
||||
sequence.addStatement(statementAsm);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the string source body representing a part of the parse tree
|
||||
*
|
||||
* @param sourceContext The parse tree context
|
||||
* @return The string source code.
|
||||
*/
|
||||
private static String getSourceBody(ParserRuleContext sourceContext) {
|
||||
Token tokenStart = StatementSource.getToken(sourceContext, true);
|
||||
Token tokenStop = StatementSource.getToken(sourceContext, false);
|
||||
CharStream stream = tokenStart.getInputStream();
|
||||
int startIndex = tokenStart.getStartIndex();
|
||||
int stopIndex = tokenStop.getStopIndex();
|
||||
Interval interval = new Interval(startIndex, stopIndex);
|
||||
return stream.getText(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all referenced symbol variables
|
||||
*
|
||||
|
@ -4,6 +4,7 @@ import dk.camelot64.kickc.asm.*;
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import kickass.KickAssembler;
|
||||
import kickass.nonasm.c64.CharToPetsciiConverter;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
@ -82,6 +83,7 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
||||
System.setOut(new PrintStream(kickAssOut));
|
||||
int asmRes = -1;
|
||||
try {
|
||||
//CharToPetsciiConverter.setCurrentEncoding("screencode_mixed");
|
||||
asmRes = KickAssembler.main2(new String[]{asmFile.getAbsolutePath(), "-o", asmPrgFile.getAbsolutePath()});
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof AssertionError && e.getMessage().contains("Invalid number of bytes in memblock!")) {
|
||||
|
@ -22,6 +22,7 @@ public class TestKickAssRun {
|
||||
ByteArrayOutputStream kickAssOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(kickAssOut));
|
||||
try {
|
||||
//CharToPetsciiConverter.setCurrentEncoding("screencode_mixed");
|
||||
KickAssembler.main2(new String[]{asmPath.toAbsolutePath().toString(), "-o", asmPrgFile.getAbsolutePath()});
|
||||
} catch (AssertionError e) {
|
||||
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
|
||||
|
@ -7,6 +7,7 @@ import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer;
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import kickass.KickAssembler;
|
||||
import kickass.nonasm.c64.CharToPetsciiConverter;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
@ -35,6 +36,17 @@ public class TestPrograms {
|
||||
public TestPrograms() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKcKaStringEncoding() throws IOException, URISyntaxException {
|
||||
compileAndCompare("kc-ka-string-encoding", log());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobalPcMultiple() throws IOException, URISyntaxException {
|
||||
compileAndCompare("global-pc-multiple", log());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStructPosFill() throws IOException, URISyntaxException {
|
||||
compileAndCompare("struct-pos-fill");
|
||||
@ -2798,7 +2810,8 @@ public class TestPrograms {
|
||||
System.setOut(new PrintStream(kickAssOut));
|
||||
int asmRes = -1;
|
||||
try {
|
||||
asmRes = KickAssembler.main2(new String[]{asmFile.getAbsolutePath(), "-log", asmLogFile.getAbsolutePath(), "-o", asmPrgFile.getAbsolutePath(), "-vicesymbols", "-showmem"});
|
||||
//CharToPetsciiConverter.setCurrentEncoding("screencode_mixed");
|
||||
asmRes = KickAssembler.main2(new String[]{asmFile.getAbsolutePath(), "-log", asmLogFile.getAbsolutePath(), "-o", asmPrgFile.getAbsolutePath(), "-vicesymbols", "-showmem", "-bytedump"});
|
||||
} catch(Throwable e) {
|
||||
fail("KickAssembling file failed! " + e.getMessage());
|
||||
} finally {
|
||||
|
23
src/test/kc/global-pc-multiple.kc
Normal file
23
src/test/kc/global-pc-multiple.kc
Normal file
@ -0,0 +1,23 @@
|
||||
// Test setting the program PC through a #pc directive
|
||||
|
||||
|
||||
const byte* BGCOL = 0xd021;
|
||||
const byte* RASTER = 0xd012;
|
||||
|
||||
#pc(0x1000)
|
||||
|
||||
void main() {
|
||||
asm { sei }
|
||||
while(true) {
|
||||
if(*RASTER<30)
|
||||
incScreen();
|
||||
else
|
||||
*BGCOL = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#pc(0x2000)
|
||||
|
||||
void incScreen() {
|
||||
*BGCOL = *RASTER;
|
||||
}
|
18
src/test/kc/kc-ka-string-encoding.kc
Normal file
18
src/test/kc/kc-ka-string-encoding.kc
Normal file
@ -0,0 +1,18 @@
|
||||
#reserve(0x16)
|
||||
#encoding(petscii_mixed)
|
||||
char[] strTemp = "v=X";
|
||||
int main(void){
|
||||
strTemp[2] = 'e';
|
||||
strTemp[3] = 0;
|
||||
asm {
|
||||
ldy #0
|
||||
loop:
|
||||
lda strTemp,y
|
||||
beq done
|
||||
jsr $FFD2
|
||||
iny
|
||||
jmp loop
|
||||
done:
|
||||
}
|
||||
return 0;
|
||||
}
|
24
src/test/ref/global-pc-multiple.asm
Normal file
24
src/test/ref/global-pc-multiple.asm
Normal file
@ -0,0 +1,24 @@
|
||||
// Test setting the program PC through a #pc directive
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $2000 "Program"
|
||||
.label BGCOL = $d021
|
||||
.label RASTER = $d012
|
||||
main: {
|
||||
sei
|
||||
b1:
|
||||
lda RASTER
|
||||
cmp #$1e
|
||||
bcc b2
|
||||
lda #0
|
||||
sta BGCOL
|
||||
jmp b1
|
||||
b2:
|
||||
jsr incScreen
|
||||
jmp b1
|
||||
}
|
||||
incScreen: {
|
||||
lda RASTER
|
||||
sta BGCOL
|
||||
rts
|
||||
}
|
28
src/test/ref/global-pc-multiple.cfg
Normal file
28
src/test/ref/global-pc-multiple.cfg
Normal file
@ -0,0 +1,28 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
asm { sei }
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2 main::@3
|
||||
[5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@1
|
||||
[6] *((const byte*) BGCOL#0) ← (byte) 0
|
||||
to:main::@1
|
||||
main::@2: scope:[main] from main::@1
|
||||
[7] phi()
|
||||
[8] call incScreen
|
||||
to:main::@1
|
||||
incScreen: scope:[incScreen] from main::@2
|
||||
[9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0)
|
||||
to:incScreen::@return
|
||||
incScreen::@return: scope:[incScreen] from incScreen
|
||||
[10] return
|
||||
to:@return
|
397
src/test/ref/global-pc-multiple.log
Normal file
397
src/test/ref/global-pc-multiple.log
Normal file
@ -0,0 +1,397 @@
|
||||
Culled Empty Block (label) main::@6
|
||||
Culled Empty Block (label) main::@3
|
||||
Culled Empty Block (label) main::@7
|
||||
Culled Empty Block (label) main::@5
|
||||
Culled Empty Block (label) main::@9
|
||||
Culled Empty Block (label) main::@10
|
||||
Culled Empty Block (label) @1
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte*) BGCOL#0 ← ((byte*)) (number) $d021
|
||||
(byte*) RASTER#0 ← ((byte*)) (number) $d012
|
||||
to:@2
|
||||
main: scope:[main] from @2
|
||||
asm { sei }
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@11 main::@8
|
||||
if(true) goto main::@2
|
||||
to:main::@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
(bool~) main::$0 ← *((byte*) RASTER#0) < (number) $1e
|
||||
if((bool~) main::$0) goto main::@4
|
||||
to:main::@8
|
||||
main::@4: scope:[main] from main::@2
|
||||
call incScreen
|
||||
to:main::@11
|
||||
main::@11: scope:[main] from main::@4
|
||||
to:main::@1
|
||||
main::@8: scope:[main] from main::@2
|
||||
*((byte*) BGCOL#0) ← (number) 0
|
||||
to:main::@1
|
||||
main::@return: scope:[main] from main::@1
|
||||
return
|
||||
to:@return
|
||||
incScreen: scope:[incScreen] from main::@4
|
||||
*((byte*) BGCOL#0) ← *((byte*) RASTER#0)
|
||||
to:incScreen::@return
|
||||
incScreen::@return: scope:[incScreen] from incScreen
|
||||
return
|
||||
to:@return
|
||||
@2: scope:[] from @begin
|
||||
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
|
||||
(byte*) BGCOL
|
||||
(byte*) BGCOL#0
|
||||
(byte*) RASTER
|
||||
(byte*) RASTER#0
|
||||
(void()) incScreen()
|
||||
(label) incScreen::@return
|
||||
(void()) main()
|
||||
(bool~) main::$0
|
||||
(label) main::@1
|
||||
(label) main::@11
|
||||
(label) main::@2
|
||||
(label) main::@4
|
||||
(label) main::@8
|
||||
(label) main::@return
|
||||
|
||||
Adding number conversion cast (unumber) $1e in (bool~) main::$0 ← *((byte*) RASTER#0) < (number) $1e
|
||||
Adding number conversion cast (unumber) 0 in *((byte*) BGCOL#0) ← (number) 0
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Inlining cast (byte*) BGCOL#0 ← (byte*)(number) $d021
|
||||
Inlining cast (byte*) RASTER#0 ← (byte*)(number) $d012
|
||||
Inlining cast *((byte*) BGCOL#0) ← (unumber)(number) 0
|
||||
Successful SSA optimization Pass2InlineCast
|
||||
Simplifying constant pointer cast (byte*) 53281
|
||||
Simplifying constant pointer cast (byte*) 53266
|
||||
Simplifying constant integer cast $1e
|
||||
Simplifying constant integer cast 0
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (byte) $1e
|
||||
Finalized unsigned number type (byte) 0
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Simple Condition (bool~) main::$0 [5] if(*((byte*) RASTER#0)<(byte) $1e) goto main::@4
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant (const byte*) BGCOL#0 = (byte*) 53281
|
||||
Constant (const byte*) RASTER#0 = (byte*) 53266
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
if() condition always true - replacing block destination [3] if(true) goto main::@2
|
||||
Successful SSA optimization Pass2ConstantIfs
|
||||
Removing unused block main::@return
|
||||
Successful SSA optimization Pass2EliminateUnusedBlocks
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @2
|
||||
Adding NOP phi() at start of @3
|
||||
Adding NOP phi() at start of @end
|
||||
Adding NOP phi() at start of main::@1
|
||||
Adding NOP phi() at start of main::@4
|
||||
Adding NOP phi() at start of main::@11
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
Calls in [main] to incScreen:10
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
Culled Empty Block (label) @3
|
||||
Culled Empty Block (label) main::@1
|
||||
Culled Empty Block (label) main::@11
|
||||
Renumbering block @2 to @1
|
||||
Renumbering block main::@2 to main::@1
|
||||
Renumbering block main::@4 to main::@2
|
||||
Renumbering block main::@8 to main::@3
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @end
|
||||
Adding NOP phi() at start of main::@2
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
asm { sei }
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2 main::@3
|
||||
[5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@1
|
||||
[6] *((const byte*) BGCOL#0) ← (byte) 0
|
||||
to:main::@1
|
||||
main::@2: scope:[main] from main::@1
|
||||
[7] phi()
|
||||
[8] call incScreen
|
||||
to:main::@1
|
||||
incScreen: scope:[incScreen] from main::@2
|
||||
[9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0)
|
||||
to:incScreen::@return
|
||||
incScreen::@return: scope:[incScreen] from incScreen
|
||||
[10] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(byte*) BGCOL
|
||||
(byte*) RASTER
|
||||
(void()) incScreen()
|
||||
(void()) main()
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
|
||||
INITIAL ASM
|
||||
Target platform is c64basic
|
||||
// File Comments
|
||||
// Test setting the program PC through a #pc directive
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
.label BGCOL = $d021
|
||||
.label RASTER = $d012
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
// @1
|
||||
b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
// @end
|
||||
bend:
|
||||
// main
|
||||
main: {
|
||||
// asm { sei }
|
||||
sei
|
||||
jmp b1
|
||||
// main::@1
|
||||
b1:
|
||||
// [5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2 -- _deref_pbuc1_lt_vbuc2_then_la1
|
||||
lda RASTER
|
||||
cmp #$1e
|
||||
bcc b2_from_b1
|
||||
jmp b3
|
||||
// main::@3
|
||||
b3:
|
||||
// [6] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta BGCOL
|
||||
jmp b1
|
||||
// [7] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
b2_from_b1:
|
||||
jmp b2
|
||||
// main::@2
|
||||
b2:
|
||||
// [8] call incScreen
|
||||
jsr incScreen
|
||||
jmp b1
|
||||
}
|
||||
// incScreen
|
||||
incScreen: {
|
||||
// [9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0) -- _deref_pbuc1=_deref_pbuc2
|
||||
lda RASTER
|
||||
sta BGCOL
|
||||
jmp breturn
|
||||
// incScreen::@return
|
||||
breturn:
|
||||
// [10] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||
Statement [6] *((const byte*) BGCOL#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||
Statement [9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0) [ ] ( main:2::incScreen:8 [ ] ) always clobbers reg byte a
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope [incScreen]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 359 combination
|
||||
Uplifting [incScreen] best 359 combination
|
||||
Uplifting [] best 359 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Test setting the program PC through a #pc directive
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
.label BGCOL = $d021
|
||||
.label RASTER = $d012
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
// @1
|
||||
b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
// @end
|
||||
bend:
|
||||
// main
|
||||
main: {
|
||||
// asm { sei }
|
||||
sei
|
||||
jmp b1
|
||||
// main::@1
|
||||
b1:
|
||||
// [5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2 -- _deref_pbuc1_lt_vbuc2_then_la1
|
||||
lda RASTER
|
||||
cmp #$1e
|
||||
bcc b2_from_b1
|
||||
jmp b3
|
||||
// main::@3
|
||||
b3:
|
||||
// [6] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta BGCOL
|
||||
jmp b1
|
||||
// [7] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
b2_from_b1:
|
||||
jmp b2
|
||||
// main::@2
|
||||
b2:
|
||||
// [8] call incScreen
|
||||
jsr incScreen
|
||||
jmp b1
|
||||
}
|
||||
// incScreen
|
||||
incScreen: {
|
||||
// [9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0) -- _deref_pbuc1=_deref_pbuc2
|
||||
lda RASTER
|
||||
sta BGCOL
|
||||
jmp breturn
|
||||
// incScreen::@return
|
||||
breturn:
|
||||
// [10] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp bend
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp b3
|
||||
Removing instruction jmp b2
|
||||
Removing instruction jmp breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Replacing label b2_from_b1 with b2
|
||||
Removing instruction b1_from_bbegin:
|
||||
Removing instruction b1:
|
||||
Removing instruction bend_from_b1:
|
||||
Removing instruction b2_from_b1:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction bend:
|
||||
Removing instruction b3:
|
||||
Removing instruction breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
Updating BasicUpstart to call main directly
|
||||
Removing instruction jsr main
|
||||
Succesful ASM optimization Pass5SkipBegin
|
||||
Removing instruction bbegin:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte*) BGCOL
|
||||
(const byte*) BGCOL#0 BGCOL = (byte*) 53281
|
||||
(byte*) RASTER
|
||||
(const byte*) RASTER#0 RASTER = (byte*) 53266
|
||||
(void()) incScreen()
|
||||
(label) incScreen::@return
|
||||
(void()) main()
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 281
|
||||
|
||||
// File Comments
|
||||
// Test setting the program PC through a #pc directive
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $2000 "Program"
|
||||
// Global Constants & labels
|
||||
.label BGCOL = $d021
|
||||
.label RASTER = $d012
|
||||
// @begin
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
// @1
|
||||
// [2] call main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
// @end
|
||||
// main
|
||||
main: {
|
||||
// asm
|
||||
// asm { sei }
|
||||
sei
|
||||
// main::@1
|
||||
b1:
|
||||
// if(*RASTER<30)
|
||||
// [5] if(*((const byte*) RASTER#0)<(byte) $1e) goto main::@2 -- _deref_pbuc1_lt_vbuc2_then_la1
|
||||
lda RASTER
|
||||
cmp #$1e
|
||||
bcc b2
|
||||
// main::@3
|
||||
// *BGCOL = 0
|
||||
// [6] *((const byte*) BGCOL#0) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta BGCOL
|
||||
jmp b1
|
||||
// [7] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
// main::@2
|
||||
b2:
|
||||
// incScreen()
|
||||
// [8] call incScreen
|
||||
jsr incScreen
|
||||
jmp b1
|
||||
}
|
||||
// incScreen
|
||||
incScreen: {
|
||||
// *BGCOL = *RASTER
|
||||
// [9] *((const byte*) BGCOL#0) ← *((const byte*) RASTER#0) -- _deref_pbuc1=_deref_pbuc2
|
||||
lda RASTER
|
||||
sta BGCOL
|
||||
// incScreen::@return
|
||||
// }
|
||||
// [10] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
14
src/test/ref/global-pc-multiple.sym
Normal file
14
src/test/ref/global-pc-multiple.sym
Normal file
@ -0,0 +1,14 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte*) BGCOL
|
||||
(const byte*) BGCOL#0 BGCOL = (byte*) 53281
|
||||
(byte*) RASTER
|
||||
(const byte*) RASTER#0 RASTER = (byte*) 53266
|
||||
(void()) incScreen()
|
||||
(label) incScreen::@return
|
||||
(void()) main()
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
|
20
src/test/ref/kc-ka-string-encoding.asm
Normal file
20
src/test/ref/kc-ka-string-encoding.asm
Normal file
@ -0,0 +1,20 @@
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
main: {
|
||||
lda #'e'
|
||||
sta strTemp+2
|
||||
lda #0
|
||||
sta strTemp+3
|
||||
tay
|
||||
loop:
|
||||
lda strTemp,y
|
||||
beq done
|
||||
jsr $ffd2
|
||||
iny
|
||||
jmp loop
|
||||
done:
|
||||
rts
|
||||
}
|
||||
.encoding "petscii_mixed"
|
||||
strTemp: .text "v=X@"
|
17
src/test/ref/kc-ka-string-encoding.cfg
Normal file
17
src/test/ref/kc-ka-string-encoding.cfg
Normal file
@ -0,0 +1,17 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'
|
||||
[5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0
|
||||
asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[7] return
|
||||
to:@return
|
301
src/test/ref/kc-ka-string-encoding.log
Normal file
301
src/test/ref/kc-ka-string-encoding.log
Normal file
@ -0,0 +1,301 @@
|
||||
Culled Empty Block (label) main::@1
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte[]) strTemp#0 ← (const string) $0
|
||||
to:@1
|
||||
main: scope:[main] from @1
|
||||
*((byte[]) strTemp#0 + (number) 2) ← (byte) 'e'
|
||||
*((byte[]) strTemp#0 + (number) 3) ← (number) 0
|
||||
asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
(signed word) main::return#0 ← (number) 0
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
(signed word) main::return#3 ← phi( main/(signed word) main::return#0 )
|
||||
(signed word) main::return#1 ← (signed word) main::return#3
|
||||
return
|
||||
to:@return
|
||||
@1: scope:[] from @begin
|
||||
call main
|
||||
(signed word) main::return#2 ← (signed word) main::return#1
|
||||
to:@2
|
||||
@2: scope:[] from @1
|
||||
to:@end
|
||||
@end: scope:[] from @2
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(const string) $0 = (string) PETSCII_MIXED"v=X@"
|
||||
(label) @1
|
||||
(label) @2
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(signed word()) main()
|
||||
(label) main::@return
|
||||
(signed word) main::return
|
||||
(signed word) main::return#0
|
||||
(signed word) main::return#1
|
||||
(signed word) main::return#2
|
||||
(signed word) main::return#3
|
||||
(byte[]) strTemp
|
||||
(byte[]) strTemp#0
|
||||
|
||||
Adding number conversion cast (unumber) 2 in *((byte[]) strTemp#0 + (number) 2) ← (byte) 'e'
|
||||
Adding number conversion cast (unumber) 0 in *((byte[]) strTemp#0 + (number) 3) ← (number) 0
|
||||
Adding number conversion cast (unumber) 3 in *((byte[]) strTemp#0 + (number) 3) ← ((unumber)) (number) 0
|
||||
Adding number conversion cast (snumber) 0 in (signed word) main::return#0 ← (number) 0
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Inlining cast *((byte[]) strTemp#0 + (unumber)(number) 3) ← (unumber)(number) 0
|
||||
Inlining cast (signed word) main::return#0 ← (snumber)(number) 0
|
||||
Successful SSA optimization Pass2InlineCast
|
||||
Simplifying constant integer cast 2
|
||||
Simplifying constant integer cast 0
|
||||
Simplifying constant integer cast 3
|
||||
Simplifying constant integer cast 0
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (byte) 2
|
||||
Finalized unsigned number type (byte) 0
|
||||
Finalized unsigned number type (byte) 3
|
||||
Finalized signed number type (signed byte) 0
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Alias (signed word) main::return#0 = (signed word) main::return#3 (signed word) main::return#1
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Constant (const byte[]) strTemp#0 = $0
|
||||
Constant (const signed word) main::return#0 = 0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Constant (const signed word) main::return#2 = main::return#0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Eliminating unused constant (const signed word) main::return#2
|
||||
Successful SSA optimization PassNEliminateUnusedVars
|
||||
Eliminating unused constant (const signed word) main::return#0
|
||||
Successful SSA optimization PassNEliminateUnusedVars
|
||||
Constant inlined $0 = (const byte[]) strTemp#0
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Consolidated array index constant in *(strTemp#0+2)
|
||||
Consolidated array index constant in *(strTemp#0+3)
|
||||
Successful SSA optimization Pass2ConstantAdditionElimination
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @2
|
||||
Adding NOP phi() at start of @end
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
Culled Empty Block (label) @2
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @end
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'
|
||||
[5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0
|
||||
asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[7] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(signed word()) main()
|
||||
(signed word) main::return
|
||||
(byte[]) strTemp
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
|
||||
INITIAL ASM
|
||||
Target platform is c64basic
|
||||
// File Comments
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
// @1
|
||||
b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
// @end
|
||||
bend:
|
||||
// main
|
||||
main: {
|
||||
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e' -- _deref_pbuc1=vbuc2
|
||||
lda #'e'
|
||||
sta strTemp+2
|
||||
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta strTemp+3
|
||||
// asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
ldy #0
|
||||
loop:
|
||||
lda strTemp,y
|
||||
beq done
|
||||
jsr $ffd2
|
||||
iny
|
||||
jmp loop
|
||||
done:
|
||||
jmp breturn
|
||||
// main::@return
|
||||
breturn:
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
.encoding "petscii_mixed"
|
||||
strTemp: .text "v=X@"
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e' [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||
Statement [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||
Statement asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: } always clobbers reg byte a reg byte x reg byte y
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 53 combination
|
||||
Uplifting [] best 53 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
// @1
|
||||
b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
// @end
|
||||
bend:
|
||||
// main
|
||||
main: {
|
||||
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e' -- _deref_pbuc1=vbuc2
|
||||
lda #'e'
|
||||
sta strTemp+2
|
||||
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta strTemp+3
|
||||
// asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
ldy #0
|
||||
loop:
|
||||
lda strTemp,y
|
||||
beq done
|
||||
jsr $ffd2
|
||||
iny
|
||||
jmp loop
|
||||
done:
|
||||
jmp breturn
|
||||
// main::@return
|
||||
breturn:
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
.encoding "petscii_mixed"
|
||||
strTemp: .text "v=X@"
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp bend
|
||||
Removing instruction jmp breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Replacing instruction ldy #0 with TAY
|
||||
Removing instruction b1_from_bbegin:
|
||||
Removing instruction b1:
|
||||
Removing instruction bend_from_b1:
|
||||
Removing instruction breturn:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction bend:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
Updating BasicUpstart to call main directly
|
||||
Removing instruction jsr main
|
||||
Succesful ASM optimization Pass5SkipBegin
|
||||
Removing instruction bbegin:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(signed word()) main()
|
||||
(label) main::@return
|
||||
(signed word) main::return
|
||||
(byte[]) strTemp
|
||||
(const byte[]) strTemp#0 strTemp = (string) PETSCII_MIXED"v=X@"
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 38
|
||||
|
||||
// File Comments
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// @begin
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
// @1
|
||||
// [2] call main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
// @end
|
||||
// main
|
||||
main: {
|
||||
// strTemp[2] = 'e'
|
||||
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e' -- _deref_pbuc1=vbuc2
|
||||
lda #'e'
|
||||
sta strTemp+2
|
||||
// strTemp[3] = 0
|
||||
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2
|
||||
lda #0
|
||||
sta strTemp+3
|
||||
// asm
|
||||
// asm { ldy#0 loop: ldastrTemp,y beqdone jsr$FFD2 iny jmploop done: }
|
||||
tay
|
||||
loop:
|
||||
lda strTemp,y
|
||||
beq done
|
||||
jsr $ffd2
|
||||
iny
|
||||
jmp loop
|
||||
done:
|
||||
// main::@return
|
||||
// }
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
.encoding "petscii_mixed"
|
||||
strTemp: .text "v=X@"
|
||||
|
9
src/test/ref/kc-ka-string-encoding.sym
Normal file
9
src/test/ref/kc-ka-string-encoding.sym
Normal file
@ -0,0 +1,9 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(signed word()) main()
|
||||
(label) main::@return
|
||||
(signed word) main::return
|
||||
(byte[]) strTemp
|
||||
(const byte[]) strTemp#0 strTemp = (string) PETSCII_MIXED"v=X@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user