1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-01-11 20:30:08 +00:00

Added support for int main(int argc, char **argv). Closes

This commit is contained in:
jespergravgaard 2021-08-05 22:33:46 +02:00
parent e58bff087e
commit dddf6db181
22 changed files with 705 additions and 1 deletions

@ -197,7 +197,29 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
startSequence.addStatement(new StatementProcedureBegin(startProcedure.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
if(initCompilation != null)
startSequence.addStatement(new StatementCall(null, SymbolRef.INIT_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
final Procedure mainProc = program.getScope().getLocalProcedure(SymbolRef.MAIN_PROC_NAME);
if(mainProc==null)
throw new CompileError("Required main() not defined in program.");
if(!SymbolType.VOID.equals(mainProc.getReturnType()) && !SymbolType.SWORD.equals(mainProc.getReturnType()))
throw new CompileError("return of main() must be 'void' or of type 'int'.", mainProc.getDefinitionSource());
if(mainProc.getParameterNames().size()==0) {
startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
} else if(mainProc.getParameterNames().size()==2) {
final List<Variable> parameters = mainProc.getParameters();
final Variable argc = parameters.get(0);
if(!SymbolType.SWORD.equals(argc.getType()))
throw new CompileError("first parameter of main() must be of type 'int'.", mainProc.getDefinitionSource());
final Variable argv = parameters.get(1);
if(!argv.getType().equals(new SymbolTypePointer(new SymbolTypePointer(SymbolType.BYTE))))
throw new CompileError("second parameter of main() must be of type 'char **'.", mainProc.getDefinitionSource());
final ArrayList<RValue> params = new ArrayList<>();
params.add(new ConstantInteger(0L, SymbolType.SWORD));
params.add(new ConstantPointer(0L, new SymbolTypePointer(SymbolType.BYTE)));
startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, params, new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
} else
throw new CompileError("main() has wrong number of parameters. It must have zero or 2 parameters.", mainProc.getDefinitionSource());
final Label startReturnLabel = startProcedure.addLabel(SymbolRef.PROCEXIT_BLOCK_NAME);
startSequence.addStatement(new StatementLabel(startReturnLabel.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
startSequence.addStatement(new StatementReturn(null, new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));

@ -14,6 +14,46 @@ public class TestProgramsFast extends TestPrograms {
// compileAndCompare("shadow-variable-error-1.c");
//}
@Test
public void testMainWrong3() throws IOException {
assertError("main-wrong-3.c", "return of main() must be 'void' or of type 'int'");
}
@Test
public void testMainWrong2() throws IOException {
assertError("main-wrong-2.c", "second parameter of main() must be of type 'char **'");
}
@Test
public void testMainWrong1() throws IOException {
assertError("main-wrong-1.c", "first parameter of main() must be of type 'int'");
}
@Test
public void testMainWrong0() throws IOException {
assertError("main-wrong-0.c", "main() has wrong number of parameters. It must have zero or 2 parameters");
}
@Test
public void testMainMissing() throws IOException {
assertError("main-missing.c", "Required main() not defined in program.", false);
}
@Test
public void testMainParam1() throws IOException {
compileAndCompare("main-param-1.c");
}
@Test
public void testMainParam0() throws IOException {
compileAndCompare("main-param-0.c");
}
@Test
public void testMainReturn() throws IOException {
compileAndCompare("main-return.c");
}
@Test
public void testNullConstant() throws IOException {
compileAndCompare("null-constant.c");

@ -0,0 +1,4 @@
// Test program with missing main()
void xmain() {
}

@ -0,0 +1,6 @@
// Test main() with parameters
int main(int argc, char **argv) {
char * SCREEN = (char*) 0x0400;
return (int)*SCREEN;
}

@ -0,0 +1,8 @@
// Test main() with parameters
int main(int argc, char **argv) {
char * SCREEN = (char*) 0x0400;
SCREEN[0] = (char)argc;
SCREEN[1] = (char) argv;
return -1;
}

@ -0,0 +1,6 @@
// Test main() with int return
int main() {
char * SCREEN = (char*) 0x0400;
return (int)*SCREEN;
}

@ -0,0 +1,5 @@
// Test program with main() with wrong number of parameters
int main(int x) {
}

@ -0,0 +1,5 @@
// Test program with main() with wrong type of parameter 0
int main(long argc, char *argv[]) {
}

@ -0,0 +1,5 @@
// Test program with main() with wrong type of parameter 0
int main(int argc, int *argv[]) {
}

@ -0,0 +1,5 @@
// Test program with main() with wrong type of return value
char main(int argc, char **argv) {
}

@ -0,0 +1,14 @@
// Test main() with parameters
// Commodore 64 PRG executable file
.file [name="main-param-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
.segment Code
main: {
// }
rts
}

@ -0,0 +1,8 @@
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return

@ -0,0 +1,158 @@
CONTROL FLOW GRAPH SSA
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from __start
main::return#0 = (signed word)*main::SCREEN
to:main::@return
main::@return: scope:[main] from main
main::return#3 = phi( main/main::return#0 )
main::return#1 = main::return#3
return
to:@return
void __start()
__start: scope:[__start] from
main::argc#0 = 0
main::argv#0 = (byte**) 0
call main
main::return#2 = main::return#1
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
void __start()
signed word main(signed word main::argc , byte** main::argv)
constant byte* main::SCREEN = (byte*)$400
signed word main::argc
signed word main::argc#0
byte** main::argv
byte** main::argv#0
signed word main::return
signed word main::return#0
signed word main::return#1
signed word main::return#2
signed word main::return#3
Simplifying constant pointer cast (byte*) 1024
Successful SSA optimization PassNCastSimplification
Alias main::return#0 = main::return#3 main::return#1
Successful SSA optimization Pass2AliasElimination
Constant main::argc#0 = 0
Constant main::argv#0 = (byte**) 0
Successful SSA optimization Pass2ConstantIdentification
Eliminating unused variable main::return#2 and assignment [3] main::return#2 = main::return#0
Eliminating unused constant main::argc#0
Eliminating unused constant main::argv#0
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused variable main::return#0 and assignment [0] main::return#0 = (signed word)*main::SCREEN
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused constant main::SCREEN
Successful SSA optimization PassNEliminateUnusedVars
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Adding NOP phi() at start of main
CALL GRAPH
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return
VARIABLE REGISTER WEIGHTS
signed word main(signed word main::argc , byte** main::argv)
signed word main::argc
byte** main::argv
signed word main::return
Initial phi equivalence classes
Complete equivalence classes
REGISTER UPLIFT POTENTIAL REGISTERS
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 36 combination
Uplifting [] best 36 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test main() with parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="main-param-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
jmp __breturn
// main::@return
__breturn:
// [1] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
signed word main(signed word main::argc , byte** main::argv)
signed word main::argc
byte** main::argv
signed word main::return
FINAL ASSEMBLER
Score: 6
// File Comments
// Test main() with parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="main-param-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
// main::@return
// }
// [1] return
rts
}
// File Data

@ -0,0 +1,5 @@
signed word main(signed word main::argc , byte** main::argv)
signed word main::argc
byte** main::argv
signed word main::return

@ -0,0 +1,20 @@
// Test main() with parameters
// Commodore 64 PRG executable file
.file [name="main-param-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
.segment Code
main: {
.label SCREEN = $400
// SCREEN[0] = (char)argc
lda #0
sta SCREEN
// SCREEN[1] = (char) argv
sta SCREEN+1
// }
rts
}

@ -0,0 +1,9 @@
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from
[0] *main::SCREEN = 0
[1] *(main::SCREEN+1) = 0
to:main::@return
main::@return: scope:[main] from main
[2] return
to:@return

@ -0,0 +1,210 @@
CONTROL FLOW GRAPH SSA
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from __start
main::argv#1 = phi( __start/main::argv#0 )
main::argc#1 = phi( __start/main::argc#0 )
main::SCREEN[0] = (byte)main::argc#1
main::SCREEN[1] = (byte)main::argv#1
main::return#0 = -1
to:main::@return
main::@return: scope:[main] from main
main::return#3 = phi( main/main::return#0 )
main::return#1 = main::return#3
return
to:@return
void __start()
__start: scope:[__start] from
main::argc#0 = 0
main::argv#0 = (byte**) 0
call main
main::return#2 = main::return#1
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
void __start()
signed word main(signed word main::argc , byte** main::argv)
constant byte* main::SCREEN = (byte*)$400
signed word main::argc
signed word main::argc#0
signed word main::argc#1
byte** main::argv
byte** main::argv#0
byte** main::argv#1
signed word main::return
signed word main::return#0
signed word main::return#1
signed word main::return#2
signed word main::return#3
Adding number conversion cast (unumber) 0 in main::SCREEN[0] = (byte)main::argc#1
Adding number conversion cast (unumber) 1 in main::SCREEN[1] = (byte)main::argv#1
Adding number conversion cast (snumber) -1 in main::return#0 = -1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast main::return#0 = (snumber)-1
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 0
Simplifying constant integer cast 1
Simplifying constant integer cast -1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) 1
Finalized signed number type (signed byte) -1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias main::return#0 = main::return#3 main::return#1
Successful SSA optimization Pass2AliasElimination
Identical Phi Values main::argc#1 main::argc#0
Identical Phi Values main::argv#1 main::argv#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant main::return#0 = -1
Constant main::argc#0 = 0
Constant main::argv#0 = (byte**) 0
Successful SSA optimization Pass2ConstantIdentification
Constant main::return#2 = main::return#0
Successful SSA optimization Pass2ConstantIdentification
Constant value identified (byte)main::argc#0 in [1] main::SCREEN[0] = (byte)main::argc#0
Constant value identified (byte)main::argv#0 in [2] main::SCREEN[1] = (byte)main::argv#0
Successful SSA optimization Pass2ConstantValues
Simplifying constant evaluating to zero (byte)main::argc#0 in [1] main::SCREEN[0] = (byte)main::argc#0
Simplifying constant evaluating to zero (byte)main::argv#0 in [2] main::SCREEN[1] = (byte)main::argv#0
Successful SSA optimization PassNSimplifyConstantZero
Simplifying expression containing zero main::SCREEN in [1] main::SCREEN[0] = 0
Successful SSA optimization PassNSimplifyExpressionWithZero
Eliminating unused constant main::argc#0
Eliminating unused constant main::argv#0
Eliminating unused constant main::return#2
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused constant main::return#0
Successful SSA optimization PassNEliminateUnusedVars
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Consolidated array index constant in *(main::SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
CALL GRAPH
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
FINAL CONTROL FLOW GRAPH
signed word main(signed word main::argc , byte** main::argv)
main: scope:[main] from
[0] *main::SCREEN = 0
[1] *(main::SCREEN+1) = 0
to:main::@return
main::@return: scope:[main] from main
[2] return
to:@return
VARIABLE REGISTER WEIGHTS
signed word main(signed word main::argc , byte** main::argv)
signed word main::argc
byte** main::argv
signed word main::return
Initial phi equivalence classes
Complete equivalence classes
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [0] *main::SCREEN = 0 [ ] ( [ ] { } ) always clobbers reg byte a
Statement [1] *(main::SCREEN+1) = 0 [ ] ( [ ] { } ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 21 combination
Uplifting [] best 21 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test main() with parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="main-param-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
.label SCREEN = $400
// [0] *main::SCREEN = 0 -- _deref_pbuc1=vbuc2
lda #0
sta SCREEN
// [1] *(main::SCREEN+1) = 0 -- _deref_pbuc1=vbuc2
lda #0
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [2] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction lda #0
Succesful ASM optimization Pass5UnnecesaryLoadElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
signed word main(signed word main::argc , byte** main::argv)
constant byte* main::SCREEN = (byte*) 1024
signed word main::argc
byte** main::argv
signed word main::return
FINAL ASSEMBLER
Score: 16
// File Comments
// Test main() with parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="main-param-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
.label SCREEN = $400
// SCREEN[0] = (char)argc
// [0] *main::SCREEN = 0 -- _deref_pbuc1=vbuc2
lda #0
sta SCREEN
// SCREEN[1] = (char) argv
// [1] *(main::SCREEN+1) = 0 -- _deref_pbuc1=vbuc2
sta SCREEN+1
// main::@return
// }
// [2] return
rts
}
// File Data

@ -0,0 +1,6 @@
signed word main(signed word main::argc , byte** main::argv)
constant byte* main::SCREEN = (byte*) 1024
signed word main::argc
byte** main::argv
signed word main::return

@ -0,0 +1,14 @@
// Test main() with int return
// Commodore 64 PRG executable file
.file [name="main-return.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
.segment Code
main: {
// }
rts
}

@ -0,0 +1,8 @@
signed word main()
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return

@ -0,0 +1,143 @@
CONTROL FLOW GRAPH SSA
signed word main()
main: scope:[main] from __start
main::return#0 = (signed word)*main::SCREEN
to:main::@return
main::@return: scope:[main] from main
main::return#3 = phi( main/main::return#0 )
main::return#1 = main::return#3
return
to:@return
void __start()
__start: scope:[__start] from
call main
main::return#2 = main::return#1
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
void __start()
signed word main()
constant byte* main::SCREEN = (byte*)$400
signed word main::return
signed word main::return#0
signed word main::return#1
signed word main::return#2
signed word main::return#3
Simplifying constant pointer cast (byte*) 1024
Successful SSA optimization PassNCastSimplification
Alias main::return#0 = main::return#3 main::return#1
Successful SSA optimization Pass2AliasElimination
Eliminating unused variable main::return#2 and assignment [3] main::return#2 = main::return#0
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused variable main::return#0 and assignment [0] main::return#0 = (signed word)*main::SCREEN
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused constant main::SCREEN
Successful SSA optimization PassNEliminateUnusedVars
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Adding NOP phi() at start of main
CALL GRAPH
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
signed word main()
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return
VARIABLE REGISTER WEIGHTS
signed word main()
signed word main::return
Initial phi equivalence classes
Complete equivalence classes
REGISTER UPLIFT POTENTIAL REGISTERS
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 36 combination
Uplifting [] best 36 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test main() with int return
// Upstart
// Commodore 64 PRG executable file
.file [name="main-return.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
jmp __breturn
// main::@return
__breturn:
// [1] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
signed word main()
signed word main::return
FINAL ASSEMBLER
Score: 6
// File Comments
// Test main() with int return
// Upstart
// Commodore 64 PRG executable file
.file [name="main-return.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
// main::@return
// }
// [1] return
rts
}
// File Data

@ -0,0 +1,3 @@
signed word main()
signed word main::return