1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-23 13:31:12 +00:00

Implemented test comparing reference output to actual compilation output

This commit is contained in:
Jesper Gravgaard 2017-07-15 22:00:02 +02:00
parent a908e4f1ca
commit 81ba13c9c3
7 changed files with 481 additions and 19 deletions

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.test; package dk.camelot64.kickc;
import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.icl.*; import dk.camelot64.kickc.icl.*;
@ -10,11 +10,34 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** Perform KickC compilation ans optimization*/ /** Perform KickC compilation and optimizations*/
public class Main { public class Compiler {
public static void main(String[] args) throws IOException {
final String fileName = "src/dk/camelot64/kickc/test/flipper-rex2.kc"; public static class CompilationResult {
final CharStream input = CharStreams.fromFileName(fileName); private AsmProgram asmProgram;
private ControlFlowGraph graph;
private Scope symbols;
public CompilationResult(AsmProgram asmProgram, ControlFlowGraph graph, Scope symbols) {
this.asmProgram = asmProgram;
this.graph = graph;
this.symbols = symbols;
}
public AsmProgram getAsmProgram() {
return asmProgram;
}
public ControlFlowGraph getGraph() {
return graph;
}
public Scope getSymbols() {
return symbols;
}
}
public CompilationResult compile(final CharStream input) throws IOException {
System.out.println(input.toString()); System.out.println(input.toString());
KickCLexer lexer = new KickCLexer(input); KickCLexer lexer = new KickCLexer(input);
KickCParser parser = new KickCParser(new CommonTokenStream(lexer)); KickCParser parser = new KickCParser(new CommonTokenStream(lexer));
@ -22,7 +45,7 @@ public class Main {
parser.addErrorListener(new BaseErrorListener() { parser.addErrorListener(new BaseErrorListener() {
@Override @Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
throw new RuntimeException("Error parsing file "+fileName+"\n - Line: "+line+"\n - Message: "+msg); throw new RuntimeException("Error parsing file "+input.getSourceName()+"\n - Line: "+line+"\n - Message: "+msg);
} }
}); });
KickCParser.FileContext file = parser.file(); KickCParser.FileContext file = parser.file();
@ -119,12 +142,7 @@ public class Main {
} }
} }
System.out.println("SYMBOLS"); return new CompilationResult(asmProgram, controlFlowGraph, programScope);
System.out.println(programScope.getSymbolTableContents());
System.out.println("CONTROL FLOW GRAPH");
System.out.println(controlFlowGraph.toString());
System.out.println("ASSEMBLER");
System.out.println(asmProgram.toString());
} }
} }

View File

@ -11,7 +11,7 @@ Features
- Add preprocessing / find a way to allow some functions to run at compile time - Add preprocessing / find a way to allow some functions to run at compile time
- Implement inline compilation of functions (and a mechanism for choosing which methods / calls to inline) - Implement inline compilation of functions (and a mechanism for choosing which methods / calls to inline)
- Add ability to call ASM code from KC. - Add ability to call ASM code from KC.
- Add ability to call KC code from ASM. (maybe declare some functions external to ensure their interface is well defined. Maybe generate ASM call stubs.) - Add ability to call KC code from ASM. (Maybe declare some functions external to ensure their interface is well defined. Maybe generate ASM call stubs.)
- Add inline ASM (maybe?) - Add inline ASM (maybe?)
- Handle long branches - Handle long branches
@ -19,6 +19,8 @@ Process/Code Structure Improvement
- Make each phase return a separate object graph (allowing for keeeping the history in memory & performing rollbacks) - Make each phase return a separate object graph (allowing for keeeping the history in memory & performing rollbacks)
- Implemenent Assertions for the output of different phases (ensuring that the result of the phase is consistent) - Implemenent Assertions for the output of different phases (ensuring that the result of the phase is consistent)
- Refactor Expression Operator Implementation & Evaluation into one class per operator - Refactor Expression Operator Implementation & Evaluation into one class per operator
- Improve error messages to give better context
- Offer to compile resulting ASM with KickAssembler
Testing Testing
- Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison. - Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison.

View File

@ -0,0 +1,101 @@
package dk.camelot64.kickc.test;
import dk.camelot64.kickc.Compiler;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/** Compile a number of source files and compare the resulting assembler with expected output*/
public class TestCompilationOutput {
private Path tempDir;
private String testPath;
private String refPath;
public TestCompilationOutput() throws IOException {
testPath = "src/dk/camelot64/kickc/test/";
refPath = "dk/camelot64/kickc/test/ref/";
tempDir = Files.createTempDirectory("kickc-output");
}
public static void main(String[] args) throws IOException, URISyntaxException {
TestCompilationOutput tester = new TestCompilationOutput();
tester.testFile("flipper-rex2");
}
private void testFile(String fileName) throws IOException, URISyntaxException {
String InputPath = testPath + fileName + ".kc";
CharStream input = CharStreams.fromFileName(InputPath);
Compiler compiler = new Compiler();
Compiler.CompilationResult output = compiler.compile(input);
assertOutput(fileName, ".sym", output.getSymbols().getSymbolTableContents());
assertOutput(fileName, ".cfg", output.getGraph().toString());
assertOutput(fileName, ".asm", output.getAsmProgram().toString());
}
private void assertOutput(
String fileName,
String extension,
String outputString) throws IOException, URISyntaxException {
// Read reference file
List<String> refLines = loadReferenceLines(fileName, extension);
// Split output into outLines
List<String> outLines = getOutLines(outputString);
for (int i = 0; i < outLines.size(); i++) {
String outLine = outLines.get(i);
if(refLines.size()>i) {
String refLine = refLines.get(i);
if(!outLine.equals(refLine)) {
System.out.println(
"Output does not match reference on line "+i+"\n"+
"Reference: "+refLine+"\n"+
"Output: "+outLine
);
writeOutputFile(fileName, extension, outputString);
System.out.println();
return;
}
}
}
}
private List<String> getOutLines(String outputString) throws IOException {
BufferedReader rdr = new BufferedReader(new StringReader(outputString));
List<String> outLines = new ArrayList<>();
for (String line = rdr.readLine(); line != null; line = rdr.readLine()) {
outLines.add(line);
}
rdr.close();
return outLines;
}
private List<String> loadReferenceLines(String fileName, String extension) throws URISyntaxException, IOException {
String refFile = refPath+fileName+extension;
ClassLoader classLoader = this.getClass().getClassLoader();
URL refResource = classLoader.getResource(refFile);
URI refURI = refResource.toURI();
return Files.readAllLines(Paths.get(refURI), Charset.defaultCharset());
}
private void writeOutputFile(String fileName, String extension, String outputString) throws IOException {
// Write output file
File file = new File(tempDir.toFile(), fileName + extension);
FileOutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
writer.write(outputString);
writer.close();
outputStream.close();
System.out.println("Output written to " + file.getAbsolutePath());
}
}

View File

@ -1,5 +1,5 @@
byte[1000] SCREEN = $0400; byte[1000] SCREEN = $0400;
byte[16*16] buffer = $1000; byte[16*16] buffer1 = $1000;
byte[16*16] buffer2 = $1100; byte[16*16] buffer2 = $1100;
byte *RASTER = $d012; byte *RASTER = $d012;
@ -20,7 +20,7 @@ do {
void prepare() { void prepare() {
byte i=0; byte i=0;
do { do {
buffer[i] = i; buffer1[i] = i;
} while (++i!=0) } while (++i!=0)
} }
@ -32,14 +32,14 @@ void flip() {
do { do {
byte c = 16; byte c = 16;
do { do {
buffer2[dstIdx] = buffer[srcIdx++]; buffer2[dstIdx] = buffer1[srcIdx++];
dstIdx = dstIdx+16; dstIdx = dstIdx+16;
} while(--c!=0) } while(--c!=0)
dstIdx--; dstIdx--;
} while(--r!=0) } while(--r!=0)
byte i=0; byte i=0;
do { do {
buffer[i] = buffer2[i]; buffer1[i] = buffer2[i];
} while (++i!=0) } while (++i!=0)
} }
@ -51,7 +51,7 @@ void plot() {
do { do {
byte x=0; byte x=0;
do { do {
line[x] = buffer[i++]; line[x] = buffer1[i++];
} while(++x<16) } while(++x<16)
line = line+40; line = line+40;
} while(--y!=0) } while(--y!=0)

View File

@ -0,0 +1,173 @@
BBEGIN:
jsr prepare
B2_from_BBEGIN:
// (byte) c#2 = (byte) 25 // xby=coby1
ldx #25
jmp B2
B2_from_B20:
// (byte) c#2 = (byte) 25 // xby=coby1
ldx #25
B2_from_B6:
// (byte) c#2 = (byte) c#1 // register copy
B2:
B3:
// (byte~) $1 * (word) 53266 // aby=_star_cowo1
lda 53266
// if((byte~) $1!=(byte) 254) goto @3 // aby_neq_coby1_then_la1
cmp #254
bne B3
B4:
// (byte~) $3 * (word) 53266 // aby=_star_cowo1
lda 53266
// if((byte~) $3!=(byte) 255) goto @4 // aby_neq_coby1_then_la1
cmp #255
bne B4
B6:
// (byte) c#1 -- (byte) c#2 // xby=_dec_xby
dex
// if((byte) c#1!=(byte) 0) goto @2 // xby_neq_0_then_la1
cpx #0
bne B2_from_B6
B7:
jsr flip
B19:
jsr plot
B20:
// if(true) goto @2 // true_then_la1
jmp B2_from_B20
BEND:
plot:
plot__B1_from_plot:
// (byte) plot::y#2 = (byte) 16 // zpby1=coby1
lda #16
sta 100
// (byte*) plot::line#2 = (word) 1236 // zpptrby1=cowo1
lda #<1236
sta 101
lda #>1236
sta 101+1
// (byte) plot::i#3 = (byte) 0 // xby=coby1
ldx #0
plot__B1_from_B15:
// (byte) plot::y#2 = (byte) plot::y#0 // register copy
// (byte*) plot::line#2 = (byte*) plot::line#0 // register copy
// (byte) plot::i#3 = (byte) plot::i#0 // register copy
plot__B1:
plot__B2_from_B1:
// (byte) plot::x#2 = (byte) 0 // yby=coby1
ldy #0
// (byte) plot::i#2 = (byte) plot::i#3 // register copy
plot__B2_from_B2:
// (byte) plot::x#2 = (byte) plot::x#0 // register copy
// (byte) plot::i#2 = (byte) plot::i#0 // register copy
plot__B2:
// (byte~) plot::$3 (word) 4096 *idx (byte) plot::i#2 // aby=cowo1_staridx_xby
lda 4096,x
// *((byte*) plot::line#2 + (byte) plot::x#2) (byte~) plot::$3 // ptr_zpptrby1_yby=aby
sta (101),y
// (byte) plot::i#0 ++ (byte) plot::i#2 // xby=_inc_xby
inx
// (byte) plot::x#0 ++ (byte) plot::x#2 // yby=_inc_yby
iny
// if((byte) plot::x#0<(byte) 16) goto plot::@2 // yby_lt_coby1_then_la1
cpy #16
bcc plot__B2_from_B2
B15:
// (byte*) plot::line#0 (byte*) plot::line#2 + (byte) 40 // zpptrby1=zpptrby1_plus_coby1
lda 101
clc
adc #40
sta 101
bcc !+
inc 101+1
!:
// (byte) plot::y#0 -- (byte) plot::y#2 // zpby1=_dec_zpby1
dec 100
// if((byte) plot::y#0!=(byte) 0) goto plot::@1 // zpby1_neq_0_then_la1
lda 100
bne plot__B1_from_B15
plot__Breturn:
rts
flip:
flip__B1_from_flip:
// (byte) flip::r#2 = (byte) 16 // zpby1=coby1
lda #16
sta 104
// (byte) flip::dstIdx#5 = (byte) 15 // yby=coby1
ldy #15
// (byte) flip::srcIdx#3 = (byte) 0 // xby=coby1
ldx #0
flip__B1_from_B11:
// (byte) flip::r#2 = (byte) flip::r#0 // register copy
// (byte) flip::dstIdx#5 = (byte) flip::dstIdx#1 // register copy
// (byte) flip::srcIdx#3 = (byte) flip::srcIdx#0 // register copy
flip__B1:
flip__B2_from_B1:
// (byte) flip::dstIdx#3 = (byte) flip::dstIdx#5 // register copy
// (byte) flip::srcIdx#2 = (byte) flip::srcIdx#3 // register copy
// (byte) flip::c#2 = (byte) 16 // zpby1=coby1
lda #16
sta 103
flip__B2_from_B2:
// (byte) flip::dstIdx#3 = (byte) flip::dstIdx#0 // register copy
// (byte) flip::srcIdx#2 = (byte) flip::srcIdx#0 // register copy
// (byte) flip::c#2 = (byte) flip::c#0 // register copy
flip__B2:
// (byte~) flip::$0 (word) 4096 *idx (byte) flip::srcIdx#2 // aby=cowo1_staridx_xby
lda 4096,x
// *((word) 4352 + (byte) flip::dstIdx#3) (byte~) flip::$0 // ptr_cowo1_yby=aby
sta 4352,y
// (byte) flip::srcIdx#0 ++ (byte) flip::srcIdx#2 // xby=_inc_xby
inx
// (byte) flip::dstIdx#0 (byte) flip::dstIdx#3 + (byte) 16 // yby=yby_plus_coby1
tya
clc
adc #16
tay
// (byte) flip::c#0 -- (byte) flip::c#2 // zpby1=_dec_zpby1
dec 103
// if((byte) flip::c#0!=(byte) 0) goto flip::@2 // zpby1_neq_0_then_la1
lda 103
bne flip__B2_from_B2
B11:
// (byte) flip::dstIdx#1 -- (byte) flip::dstIdx#0 // yby=_dec_yby
dey
// (byte) flip::r#0 -- (byte) flip::r#2 // zpby1=_dec_zpby1
dec 104
// if((byte) flip::r#0!=(byte) 0) goto flip::@1 // zpby1_neq_0_then_la1
lda 104
bne flip__B1_from_B11
flip__B3_from_B11:
// (byte) flip::i#2 = (byte) 0 // xby=coby1
ldx #0
flip__B3_from_B3:
// (byte) flip::i#2 = (byte) flip::i#1 // register copy
flip__B3:
// (byte~) flip::$4 (word) 4352 *idx (byte) flip::i#2 // aby=cowo1_staridx_xby
lda 4352,x
// *((word) 4096 + (byte) flip::i#2) (byte~) flip::$4 // ptr_cowo1_xby=aby
sta 4096,x
// (byte) flip::i#1 ++ (byte) flip::i#2 // xby=_inc_xby
inx
// if((byte) flip::i#1!=(byte) 0) goto flip::@3 // xby_neq_0_then_la1
cpx #0
bne flip__B3_from_B3
flip__Breturn:
rts
prepare:
prepare__B1_from_prepare:
// (byte) prepare::i#2 = (byte) 0 // xby=coby1
ldx #0
prepare__B1_from_B1:
// (byte) prepare::i#2 = (byte) prepare::i#0 // register copy
prepare__B1:
// *((word) 4096 + (byte) prepare::i#2) (byte) prepare::i#2 // ptr_cowo1_xby=xby
txa
sta 4096,x
// (byte) prepare::i#0 ++ (byte) prepare::i#2 // xby=_inc_xby
inx
// if((byte) prepare::i#0!=(byte) 0) goto prepare::@1 // xby_neq_0_then_la1
cpx #0
bne prepare__B1_from_B1
prepare__Breturn:
rts

View File

@ -0,0 +1,96 @@
@2: from @20 @6 @BEGIN
(byte) c#2 ← phi( @20/(byte) 25 @6/(byte) c#1 @BEGIN/(byte) 25 )
to:@3
@BEGIN: from
call prepare param-assignment
to:@2
@20: from @19
if(true) goto @2
to:@END
prepare::@1: from prepare prepare::@1
(byte) prepare::i#2 ← phi( prepare/(byte) 0 prepare::@1/(byte) prepare::i#0 )
*((word) 4096 + (byte) prepare::i#2) ← (byte) prepare::i#2
(byte) prepare::i#0 ← ++ (byte) prepare::i#2
if((byte) prepare::i#0!=(byte) 0) goto prepare::@1
to:prepare::@return
flip::@2: from flip::@1 flip::@2
(byte) flip::dstIdx#3 ← phi( flip::@1/(byte) flip::dstIdx#5 flip::@2/(byte) flip::dstIdx#0 )
(byte) flip::srcIdx#2 ← phi( flip::@1/(byte) flip::srcIdx#3 flip::@2/(byte) flip::srcIdx#0 )
(byte) flip::c#2 ← phi( flip::@1/(byte) 16 flip::@2/(byte) flip::c#0 )
(byte~) flip::$0 ← (word) 4096 *idx (byte) flip::srcIdx#2
*((word) 4352 + (byte) flip::dstIdx#3) ← (byte~) flip::$0
(byte) flip::srcIdx#0 ← ++ (byte) flip::srcIdx#2
(byte) flip::dstIdx#0 ← (byte) flip::dstIdx#3 + (byte) 16
(byte) flip::c#0 ← -- (byte) flip::c#2
if((byte) flip::c#0!=(byte) 0) goto flip::@2
to:@11
@4: from @3 @4
(byte~) $3 ← * (word) 53266
if((byte~) $3!=(byte) 255) goto @4
to:@6
@11: from flip::@2
(byte) flip::dstIdx#1 ← -- (byte) flip::dstIdx#0
(byte) flip::r#0 ← -- (byte) flip::r#2
if((byte) flip::r#0!=(byte) 0) goto flip::@1
to:flip::@3
plot::@return: from @15
return
to:@RETURN
plot::@2: from plot::@1 plot::@2
(byte) plot::x#2 ← phi( plot::@1/(byte) 0 plot::@2/(byte) plot::x#0 )
(byte) plot::i#2 ← phi( plot::@1/(byte) plot::i#3 plot::@2/(byte) plot::i#0 )
(byte~) plot::$3 ← (word) 4096 *idx (byte) plot::i#2
*((byte*) plot::line#2 + (byte) plot::x#2) ← (byte~) plot::$3
(byte) plot::i#0 ← ++ (byte) plot::i#2
(byte) plot::x#0 ← ++ (byte) plot::x#2
if((byte) plot::x#0<(byte) 16) goto plot::@2
to:@15
@6: from @4
(byte) c#1 ← -- (byte) c#2
if((byte) c#1!=(byte) 0) goto @2
to:@7
plot::@1: from @15 plot
(byte) plot::y#2 ← phi( @15/(byte) plot::y#0 plot/(byte) 16 )
(byte*) plot::line#2 ← phi( @15/(byte*) plot::line#0 plot/(word) 1236 )
(byte) plot::i#3 ← phi( @15/(byte) plot::i#0 plot/(byte) 0 )
to:plot::@2
prepare::@return: from prepare::@1
return
to:@RETURN
flip: from @7
to:flip::@1
prepare: from @BEGIN
to:prepare::@1
@7: from @6
call flip param-assignment
to:@19
flip::@1: from @11 flip
(byte) flip::r#2 ← phi( @11/(byte) flip::r#0 flip/(byte) 16 )
(byte) flip::dstIdx#5 ← phi( @11/(byte) flip::dstIdx#1 flip/(byte) 15 )
(byte) flip::srcIdx#3 ← phi( @11/(byte) flip::srcIdx#0 flip/(byte) 0 )
to:flip::@2
@15: from plot::@2
(byte*) plot::line#0 ← (byte*) plot::line#2 + (byte) 40
(byte) plot::y#0 ← -- (byte) plot::y#2
if((byte) plot::y#0!=(byte) 0) goto plot::@1
to:plot::@return
flip::@3: from @11 flip::@3
(byte) flip::i#2 ← phi( @11/(byte) 0 flip::@3/(byte) flip::i#1 )
(byte~) flip::$4 ← (word) 4352 *idx (byte) flip::i#2
*((word) 4096 + (byte) flip::i#2) ← (byte~) flip::$4
(byte) flip::i#1 ← ++ (byte) flip::i#2
if((byte) flip::i#1!=(byte) 0) goto flip::@3
to:flip::@return
@END: from @20
@19: from @7
call plot param-assignment
to:@20
plot: from @19
to:plot::@1
@3: from @2 @3
(byte~) $1 ← * (word) 53266
if((byte~) $1!=(byte) 254) goto @3
to:@4
flip::@return: from flip::@3
return
to:@RETURN

View File

@ -0,0 +1,72 @@
(byte~) $1 reg byte a
(byte~) $3 reg byte a
(label) @11
(label) @15
(label) @19
(label) @2
(label) @20
(label) @3
(label) @4
(label) @6
(label) @7
(label) @BEGIN
(label) @END
(byte*) RASTER
(byte[1000]) SCREEN
(byte[256]) buffer1
(byte[256]) buffer2
(byte) c
(byte) c#1 reg byte x
(byte) c#2 reg byte x
(void()) flip()
(byte~) flip::$0 reg byte a
(byte~) flip::$4 reg byte a
(label) flip::@1
(label) flip::@2
(label) flip::@3
(label) flip::@return
(byte) flip::c
(byte) flip::c#0 zp byte:103
(byte) flip::c#2 zp byte:103
(byte) flip::dstIdx
(byte) flip::dstIdx#0 reg byte y
(byte) flip::dstIdx#1 reg byte y
(byte) flip::dstIdx#3 reg byte y
(byte) flip::dstIdx#5 reg byte y
(byte) flip::i
(byte) flip::i#1 reg byte x
(byte) flip::i#2 reg byte x
(byte) flip::r
(byte) flip::r#0 zp byte:104
(byte) flip::r#2 zp byte:104
(byte) flip::srcIdx
(byte) flip::srcIdx#0 reg byte x
(byte) flip::srcIdx#2 reg byte x
(byte) flip::srcIdx#3 reg byte x
(void()) plot()
(byte~) plot::$3 reg byte a
(label) plot::@1
(label) plot::@2
(label) plot::@return
(byte) plot::i
(byte) plot::i#0 reg byte x
(byte) plot::i#2 reg byte x
(byte) plot::i#3 reg byte x
(byte*) plot::line
(byte*) plot::line#0 zp ptr byte:101
(byte*) plot::line#2 zp ptr byte:101
(byte) plot::x
(byte) plot::x#0 reg byte y
(byte) plot::x#2 reg byte y
(byte) plot::y
(byte) plot::y#0 zp byte:100
(byte) plot::y#2 zp byte:100
(void()) prepare()
(label) prepare::@1
(label) prepare::@return
(byte) prepare::i
(byte) prepare::i#0 reg byte x
(byte) prepare::i#2 reg byte x