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

Preventing double imports. Closes #102

This commit is contained in:
jespergravgaard 2017-12-02 16:56:58 +01:00
parent 41fd3bc436
commit 9c39311d67
8 changed files with 612 additions and 54 deletions

View File

@ -19,10 +19,9 @@ public class Compiler {
private Program program;
public Compiler() {
this.program = new Program(new ProgramScope(), new CompileLog());
this.program.setImportPaths(new ArrayList<>());
this.program = new Program();
}
public CompileLog getLog() {
return program.getLog();
}
@ -57,45 +56,51 @@ public class Compiler {
}
public static void loadAndParseFile(String fileName, Program program, StatementSequenceGenerator statementSequenceGenerator) {
CharStream fileStream = loadFile(fileName, program);
program.getLog().append(fileStream.toString());
KickCLexer lexer = new KickCLexer(fileStream);
KickCParser parser = new KickCParser(new CommonTokenStream(lexer));
parser.setBuildParseTree(true);
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(
Recognizer<?, ?> recognizer,
Object offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
throw new CompileError("Error parsing file " + fileStream.getSourceName() + "\n - Line: " + line + "\n - Message: " + msg);
}
});
statementSequenceGenerator.generate(parser.file());
}
private static CharStream loadFile(String fileName, Program program) {
if(!fileName.endsWith(".kc")) {
fileName += ".kc";
}
try {
List<String> importPaths = program.getImportPaths();
for (String importPath : importPaths) {
if(!importPath.endsWith("/")) {
importPath += "/";
}
String filePath = importPath + fileName;
File file = new File(filePath);
if(file.exists()) {
return CharStreams.fromPath(file.toPath());
}
File file = loadFile(fileName, program);
List<String> imported = program.getImported();
if (imported.contains(file.getAbsolutePath())) {
return;
}
final CharStream fileStream = CharStreams.fromPath(file.toPath());
imported.add(file.getAbsolutePath());
program.getLog().append(fileStream.toString());
KickCLexer lexer = new KickCLexer(fileStream);
KickCParser parser = new KickCParser(new CommonTokenStream(lexer));
parser.setBuildParseTree(true);
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(
Recognizer<?, ?> recognizer,
Object offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
throw new CompileError("Error parsing file " + fileStream.getSourceName() + "\n - Line: " + line + "\n - Message: " + msg);
}
});
statementSequenceGenerator.generate(parser.file());
} catch (IOException e) {
throw new CompileError("Error loading file " + fileName, e);
}
}
private static File loadFile(String fileName, Program program) {
if (!fileName.endsWith(".kc")) {
fileName += ".kc";
}
List<String> importPaths = program.getImportPaths();
for (String importPath : importPaths) {
if (!importPath.endsWith("/")) {
importPath += "/";
}
String filePath = importPath + fileName;
File file = new File(filePath);
if (file.exists()) {
return file;
}
}
throw new CompileError("File not found " + fileName);
}

View File

@ -1,10 +1,9 @@
package dk.camelot64.kickc.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.asm.AsmProgram;
import java.util.ArrayList;
import java.util.List;
/** A KickC Intermediate Compiler Language (ICL) Program */
@ -12,6 +11,8 @@ public class Program {
/** Paths used for importing files. */
private List<String> importPaths;
/** Imported files. */
private List<String> imported;
/** The initial statement sequence generated byt the parser. */
private StatementSequence statementSequence;
/** The main scope. */
@ -52,27 +53,19 @@ public class Program {
/** Separation of live range equivalence classes into scopes - used for register uplift */
private RegisterUpliftProgram registerUpliftProgram;
@JsonCreator
public Program(
@JsonProperty("scope") ProgramScope scope,
@JsonProperty("graph") ControlFlowGraph graph,
@JsonProperty("asm") AsmProgram asm) {
this.scope = scope;
this.graph = graph;
this.asm = asm;
}
public Program(ProgramScope programScope, CompileLog log) {
this.scope = programScope;
this.log = log;
public Program() {
this.scope = new ProgramScope();
this.log = new CompileLog();
this.importPaths = new ArrayList<>();
this.imported = new ArrayList<>();
}
public List<String> getImportPaths() {
return importPaths;
}
public void setImportPaths(List<String> importPaths) {
this.importPaths = importPaths;
public List<String> getImported() {
return imported;
}
public StatementSequence getStatementSequence() {

View File

@ -24,6 +24,10 @@ public class TestPrograms extends TestCase {
helper = new ReferenceHelper("dk/camelot64/kickc/test/ref/");
}
public void testDoubleImport() throws IOException, URISyntaxException {
compileAndCompare("double-import");
}
public void testImporting() throws IOException, URISyntaxException {
compileAndCompare("importing");
}

View File

@ -0,0 +1,6 @@
import "imported"
import "imported"
void main() {
*BGCOL = RED;
}

View File

@ -0,0 +1,11 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.const BGCOL = $d021
.const RED = 2
jsr main
main: {
lda #RED
sta BGCOL
rts
}

View File

@ -0,0 +1,15 @@
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @1
[3] phi() [ ] ( )
main: scope:[main] from @1
[4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] )
to:main::@return
main::@return: scope:[main] from main
[5] return [ ] ( main:2 [ ] )
to:@return

View File

@ -0,0 +1,514 @@
import "imported"
import "imported"
void main() {
*BGCOL = RED;
}
Importing imported
const byte *BGCOL = $d021;
const byte RED = 2;
Importing imported
PROGRAM
(byte*) BGCOL ← (word) 53281
(byte) RED ← (byte/signed byte/word/signed word) 2
proc (void()) main()
*((byte*) BGCOL) ← (byte) RED
main::@return:
return
endproc // main()
call main
SYMBOLS
(byte*) BGCOL
(byte) RED
(void()) main()
(label) main::@return
Promoting word to byte* in BGCOL ← ((byte*)) 53281
INITIAL CONTROL FLOW GRAPH
@begin: scope:[] from
(byte*) BGCOL ← ((byte*)) (word) 53281
(byte) RED ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from
*((byte*) BGCOL) ← (byte) RED
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main
to:@end
@end: scope:[] from @1
CONTROL FLOW GRAPH
@begin: scope:[] from
(byte*) BGCOL ← ((byte*)) (word) 53281
(byte) RED ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from
*((byte*) BGCOL) ← (byte) RED
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main
to:@end
@end: scope:[] from @1
PROCEDURE MODIFY VARIABLE ANALYSIS
CONTROL FLOW GRAPH WITH ASSIGNMENT CALL
@begin: scope:[] from
(byte*) BGCOL ← ((byte*)) (word) 53281
(byte) RED ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
*((byte*) BGCOL) ← (byte) RED
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main param-assignment
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
Completing Phi functions...
Completing Phi functions...
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte*) BGCOL#0 ← ((byte*)) (word) 53281
(byte) RED#0 ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
(byte*) BGCOL#1 ← phi( @1/(byte*) BGCOL#2 )
(byte) RED#1 ← phi( @1/(byte) RED#2 )
*((byte*) BGCOL#1) ← (byte) RED#1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
(byte*) BGCOL#2 ← phi( @begin/(byte*) BGCOL#0 )
(byte) RED#2 ← phi( @begin/(byte) RED#0 )
call main param-assignment
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
CONTROL FLOW GRAPH WITH ASSIGNMENT CALL & RETURN
@begin: scope:[] from
(byte*) BGCOL#0 ← ((byte*)) (word) 53281
(byte) RED#0 ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
(byte*) BGCOL#1 ← phi( @1/(byte*) BGCOL#2 )
(byte) RED#1 ← phi( @1/(byte) RED#2 )
*((byte*) BGCOL#1) ← (byte) RED#1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
(byte*) BGCOL#2 ← phi( @begin/(byte*) BGCOL#0 )
(byte) RED#2 ← phi( @begin/(byte) RED#0 )
call main param-assignment
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
INITIAL SSA SYMBOL TABLE
(label) @1
(label) @2
(label) @begin
(label) @end
(byte*) BGCOL
(byte*) BGCOL#0
(byte*) BGCOL#1
(byte*) BGCOL#2
(byte) RED
(byte) RED#0
(byte) RED#1
(byte) RED#2
(void()) main()
(label) main::@return
Culled Empty Block (label) @2
Succesful SSA optimization Pass2CullEmptyBlocks
CONTROL FLOW GRAPH
@begin: scope:[] from
(byte*) BGCOL#0 ← ((byte*)) (word) 53281
(byte) RED#0 ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
(byte*) BGCOL#1 ← phi( @1/(byte*) BGCOL#2 )
(byte) RED#1 ← phi( @1/(byte) RED#2 )
*((byte*) BGCOL#1) ← (byte) RED#1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
(byte*) BGCOL#2 ← phi( @begin/(byte*) BGCOL#0 )
(byte) RED#2 ← phi( @begin/(byte) RED#0 )
call main param-assignment
to:@end
@end: scope:[] from @1
Not aliassing across scopes: RED#1 RED#2
Not aliassing across scopes: BGCOL#1 BGCOL#2
Alias (byte) RED#0 = (byte) RED#2
Alias (byte*) BGCOL#0 = (byte*) BGCOL#2
Succesful SSA optimization Pass2AliasElimination
CONTROL FLOW GRAPH
@begin: scope:[] from
(byte*) BGCOL#0 ← ((byte*)) (word) 53281
(byte) RED#0 ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
(byte*) BGCOL#1 ← phi( @1/(byte*) BGCOL#0 )
(byte) RED#1 ← phi( @1/(byte) RED#0 )
*((byte*) BGCOL#1) ← (byte) RED#1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main param-assignment
to:@end
@end: scope:[] from @1
Not aliassing across scopes: RED#1 RED#0
Not aliassing across scopes: BGCOL#1 BGCOL#0
Redundant Phi (byte) RED#1 (byte) RED#0
Redundant Phi (byte*) BGCOL#1 (byte*) BGCOL#0
Succesful SSA optimization Pass2RedundantPhiElimination
CONTROL FLOW GRAPH
@begin: scope:[] from
(byte*) BGCOL#0 ← ((byte*)) (word) 53281
(byte) RED#0 ← (byte/signed byte/word/signed word) 2
to:@1
main: scope:[main] from @1
*((byte*) BGCOL#0) ← (byte) RED#0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main param-assignment
to:@end
@end: scope:[] from @1
Constant (const byte*) BGCOL#0 = ((byte*))53281
Constant (const byte) RED#0 = 2
Succesful SSA optimization Pass2ConstantIdentification
CONTROL FLOW GRAPH
@begin: scope:[] from
to:@1
main: scope:[main] from @1
*((const byte*) BGCOL#0) ← (const byte) RED#0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main param-assignment
to:@end
@end: scope:[] from @1
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte*) BGCOL
(const byte*) BGCOL#0 = ((byte*))(word) 53281
(byte) RED
(const byte) RED#0 = (byte/signed byte/word/signed word) 2
(void()) main()
(label) main::@return
Block Sequence Planned @begin @1 @end main main::@return
Block Sequence Planned @begin @1 @end main main::@return
CONTROL FLOW GRAPH - PHI LIFTED
@begin: scope:[] from
to:@1
@1: scope:[] from @begin
call main param-assignment
to:@end
@end: scope:[] from @1
main: scope:[main] from @1
*((const byte*) BGCOL#0) ← (const byte) RED#0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Propagating live ranges...
CONTROL FLOW GRAPH - LIVE RANGES FOUND
@begin: scope:[] from
[0] phi() [ ]
to:@1
@1: scope:[] from @begin
[1] phi() [ ]
[2] call main param-assignment [ ]
to:@end
@end: scope:[] from @1
[3] phi() [ ]
main: scope:[main] from @1
[4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ]
to:main::@return
main::@return: scope:[main] from main
[5] return [ ]
to:@return
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Block Sequence Planned @begin @1 @end main main::@return
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Propagating live ranges...
CONTROL FLOW GRAPH - BEFORE EFFECTIVE LIVE RANGES
@begin: scope:[] from
[0] phi() [ ]
to:@1
@1: scope:[] from @begin
[1] phi() [ ]
[2] call main param-assignment [ ]
to:@end
@end: scope:[] from @1
[3] phi() [ ]
main: scope:[main] from @1
[4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ]
to:main::@return
main::@return: scope:[main] from main
[5] return [ ]
to:@return
CONTROL FLOW GRAPH - PHI MEM COALESCED
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @1
[3] phi() [ ] ( )
main: scope:[main] from @1
[4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] )
to:main::@return
main::@return: scope:[main] from main
[5] return [ ] ( main:2 [ ] )
to:@return
DOMINATORS
@begin dominated by @begin
@1 dominated by @1 @begin
@end dominated by @1 @begin @end
main dominated by @1 @begin main
main::@return dominated by main::@return @1 @begin main
NATURAL LOOPS
Found 0 loops in scope []
Found 0 loops in scope [main]
NATURAL LOOPS WITH DEPTH
VARIABLE REGISTER WEIGHTS
(byte*) BGCOL
(byte) RED
(void()) main()
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.const BGCOL = $d021
.const RED = 2
//SEG2 @begin
bbegin:
//SEG3 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG4 @1
b1:
//SEG5 [2] call main param-assignment [ ] ( )
jsr main
//SEG6 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG7 @end
bend:
//SEG8 main
main: {
//SEG9 [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) -- _deref_cowo1=coby2
lda #RED
sta BGCOL
jmp breturn
//SEG10 main::@return
breturn:
//SEG11 [5] return [ ] ( main:2 [ ] )
rts
}
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 27 combination
Uplifting [] best 27 combination
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
ASSEMBLER
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.const BGCOL = $d021
.const RED = 2
//SEG2 @begin
bbegin:
//SEG3 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
//SEG4 @1
b1:
//SEG5 [2] call main param-assignment [ ] ( )
jsr main
//SEG6 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
//SEG7 @end
bend:
//SEG8 main
main: {
//SEG9 [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) -- _deref_cowo1=coby2
lda #RED
sta BGCOL
//SEG10 main::@return
breturn:
//SEG11 [5] return [ ] ( main:2 [ ] )
rts
}
Removing instruction bbegin:
Removing instruction b1_from_bbegin:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
ASSEMBLER
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.const BGCOL = $d021
.const RED = 2
//SEG2 @begin
//SEG3 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG4 @1
b1:
//SEG5 [2] call main param-assignment [ ] ( )
jsr main
//SEG6 [3] phi from @1 to @end [phi:@1->@end]
//SEG7 @end
bend:
//SEG8 main
main: {
//SEG9 [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) -- _deref_cowo1=coby2
lda #RED
sta BGCOL
//SEG10 main::@return
breturn:
//SEG11 [5] return [ ] ( main:2 [ ] )
rts
}
Removing instruction b1:
Removing instruction bend:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
ASSEMBLER
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.const BGCOL = $d021
.const RED = 2
//SEG2 @begin
//SEG3 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG4 @1
//SEG5 [2] call main param-assignment [ ] ( )
jsr main
//SEG6 [3] phi from @1 to @end [phi:@1->@end]
//SEG7 @end
//SEG8 main
main: {
//SEG9 [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) -- _deref_cowo1=coby2
lda #RED
sta BGCOL
//SEG10 main::@return
//SEG11 [5] return [ ] ( main:2 [ ] )
rts
}
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte*) BGCOL
(const byte*) BGCOL#0 BGCOL = ((byte*))(word) 53281
(byte) RED
(const byte) RED#0 RED = (byte/signed byte/word/signed word) 2
(void()) main()
(label) main::@return
FINAL CODE
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
.const BGCOL = $d021
.const RED = 2
//SEG2 @begin
//SEG3 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG4 @1
//SEG5 [2] call main param-assignment [ ] ( )
jsr main
//SEG6 [3] phi from @1 to @end [phi:@1->@end]
//SEG7 @end
//SEG8 main
main: {
//SEG9 [4] *((const byte*) BGCOL#0) ← (const byte) RED#0 [ ] ( main:2 [ ] ) -- _deref_cowo1=coby2
lda #RED
sta BGCOL
//SEG10 main::@return
//SEG11 [5] return [ ] ( main:2 [ ] )
rts
}

View File

@ -0,0 +1,10 @@
(label) @1
(label) @begin
(label) @end
(byte*) BGCOL
(const byte*) BGCOL#0 BGCOL = ((byte*))(word) 53281
(byte) RED
(const byte) RED#0 RED = (byte/signed byte/word/signed word) 2
(void()) main()
(label) main::@return