1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Snapshotting program dynamic state now working through Serialization. Working on #246

This commit is contained in:
jespergravgaard 2019-08-03 13:22:27 +02:00
parent aceea6d8ec
commit 198280e1c8
32 changed files with 1045 additions and 72 deletions

View 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();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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<>();

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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 "";
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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
*

View File

@ -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!")) {

View File

@ -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)));

View File

@ -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 {

View 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;
}

View 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;
}

View 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
}

View 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

View 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

View 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

View 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@"

View 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

View 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@"

View 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@"