1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-24 20:32:39 +00:00

Added support for empty statements. Closes #395

This commit is contained in:
jespergravgaard 2020-06-28 00:16:25 +02:00
parent b195a3c545
commit 75e71b5bbb
16 changed files with 944 additions and 500 deletions

View File

@ -17728,3 +17728,7 @@ tax
//FRAGMENT vbuyy=_byte_pwuc1_derefidx_vbuyy
lda {c1},y
tay
//FRAGMENT vbuc1_ge_vbuaa_then_la1
cmp #{c1}
bcc {la1}
beq {la1}

View File

@ -163,7 +163,7 @@ public class StatementSource implements Serializable {
return new StatementSource(nodeStart, nodeStop);
}
public static StatementSource kickAsm(KickCParser.DeclKasmContext ctx) {
public static StatementSource kickAsm(KickCParser.KasmContentContext ctx) {
ParseTree nodeStart = ctx.getChild(0);
ParseTree nodeStop = ctx.getChild(0);
return new StatementSource(nodeStart, nodeStop);

View File

@ -53,7 +53,7 @@ typeDef
declVariableInit
: NAME declArray* ('=' expr)? #declVariableInitExpr
| NAME declArray* '=' declKasm #declVariableInitKasm
| NAME declArray* '=' kasmContent #declVariableInitKasm
;
declType
@ -189,7 +189,8 @@ stmt
| BREAK ';' #stmtBreak
| CONTINUE ';' #stmtContinue
| ASM asmDirectives? CURLY_BEGIN asmLines ASM_CURLY_END #stmtAsm
| declKasm #stmtDeclKasm
| kasmContent #stmtDeclKasm
| ';' #stmtEmpty
;
switchCases:
@ -254,7 +255,7 @@ parameterList
: expr (COMMA expr)*
;
declKasm
kasmContent
: KICKASM asmDirectives? KICKASM_BODY
;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -997,6 +997,18 @@ public class KickCParserBaseListener implements KickCParserListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtDeclKasm(KickCParser.StmtDeclKasmContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStmtEmpty(KickCParser.StmtEmptyContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStmtEmpty(KickCParser.StmtEmptyContext ctx) { }
/**
* {@inheritDoc}
*
@ -1386,13 +1398,13 @@ public class KickCParserBaseListener implements KickCParserListener {
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDeclKasm(KickCParser.DeclKasmContext ctx) { }
@Override public void enterKasmContent(KickCParser.KasmContentContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDeclKasm(KickCParser.DeclKasmContext ctx) { }
@Override public void exitKasmContent(KickCParser.KasmContentContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -587,6 +587,13 @@ public class KickCParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> imple
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtDeclKasm(KickCParser.StmtDeclKasmContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitStmtEmpty(KickCParser.StmtEmptyContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@ -817,7 +824,7 @@ public class KickCParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> imple
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDeclKasm(KickCParser.DeclKasmContext ctx) { return visitChildren(ctx); }
@Override public T visitKasmContent(KickCParser.KasmContentContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -949,6 +949,18 @@ public interface KickCParserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitStmtDeclKasm(KickCParser.StmtDeclKasmContext ctx);
/**
* Enter a parse tree produced by the {@code stmtEmpty}
* labeled alternative in {@link KickCParser#stmt}.
* @param ctx the parse tree
*/
void enterStmtEmpty(KickCParser.StmtEmptyContext ctx);
/**
* Exit a parse tree produced by the {@code stmtEmpty}
* labeled alternative in {@link KickCParser#stmt}.
* @param ctx the parse tree
*/
void exitStmtEmpty(KickCParser.StmtEmptyContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#switchCases}.
* @param ctx the parse tree
@ -1328,15 +1340,15 @@ public interface KickCParserListener extends ParseTreeListener {
*/
void exitParameterList(KickCParser.ParameterListContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#declKasm}.
* Enter a parse tree produced by {@link KickCParser#kasmContent}.
* @param ctx the parse tree
*/
void enterDeclKasm(KickCParser.DeclKasmContext ctx);
void enterKasmContent(KickCParser.KasmContentContext ctx);
/**
* Exit a parse tree produced by {@link KickCParser#declKasm}.
* Exit a parse tree produced by {@link KickCParser#kasmContent}.
* @param ctx the parse tree
*/
void exitDeclKasm(KickCParser.DeclKasmContext ctx);
void exitKasmContent(KickCParser.KasmContentContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#asmDirectives}.
* @param ctx the parse tree

View File

@ -564,6 +564,13 @@ public interface KickCParserVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitStmtDeclKasm(KickCParser.StmtDeclKasmContext ctx);
/**
* Visit a parse tree produced by the {@code stmtEmpty}
* labeled alternative in {@link KickCParser#stmt}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitStmtEmpty(KickCParser.StmtEmptyContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#switchCases}.
* @param ctx the parse tree
@ -786,11 +793,11 @@ public interface KickCParserVisitor<T> extends ParseTreeVisitor<T> {
*/
T visitParameterList(KickCParser.ParameterListContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#declKasm}.
* Visit a parse tree produced by {@link KickCParser#kasmContent}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDeclKasm(KickCParser.DeclKasmContext ctx);
T visitKasmContent(KickCParser.KasmContentContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#asmDirectives}.
* @param ctx the parse tree

View File

@ -442,8 +442,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
@Override
public Object visitStmtDeclKasm(KickCParser.StmtDeclKasmContext ctx) {
final KickAsm kickAsm = (KickAsm) this.visit(ctx.declKasm());
StatementKickAsm statementKickAsm = new StatementKickAsm(kickAsm.kickAsmCode, kickAsm.bytes, kickAsm.cycles, kickAsm.uses, kickAsm.declaredClobber, StatementSource.kickAsm(ctx.declKasm()), ensureUnusedComments(getCommentsSymbol(ctx)));
final KickAsm kickAsm = (KickAsm) this.visit(ctx.kasmContent());
StatementKickAsm statementKickAsm = new StatementKickAsm(kickAsm.kickAsmCode, kickAsm.bytes, kickAsm.cycles, kickAsm.uses, kickAsm.declaredClobber, StatementSource.kickAsm(ctx.kasmContent()), ensureUnusedComments(getCommentsSymbol(ctx)));
addStatement(statementKickAsm);
return statementKickAsm;
}
@ -467,7 +467,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
@Override
public KickAsm visitDeclKasm(KickCParser.DeclKasmContext ctx) {
public Object visitKasmContent(KickCParser.KasmContentContext ctx) {
String kasmBody = ctx.KICKASM_BODY().getText();
Pattern p = Pattern.compile("\\{\\{[\\s]*(.*)[\\s]*\\}\\}", Pattern.DOTALL);
Matcher m = p.matcher(kasmBody);
@ -1036,7 +1036,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
throw new CompileError("KickAsm initializers only supported for arrays " + varDecl.getEffectiveType().getTypeName(), statementSource);
}
// Add KickAsm statement
KickAsm kasm = (KickAsm) this.visit(ctx.declKasm());
KickAsm kasm = (KickAsm) this.visit(ctx.kasmContent());
if(kasm.cycles != null) {
throw new CompileError("KickAsm initializers does not support 'cycles' directive.", statementSource);
}

View File

@ -42,6 +42,11 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testStmtEmpty() throws IOException, URISyntaxException {
compileAndCompare("stmt-empty.c");
}
@Test
public void testEmptyFunction2() throws IOException, URISyntaxException {
compileAndCompare("empty-function-2.c");

10
src/test/kc/stmt-empty.c Normal file
View File

@ -0,0 +1,10 @@
// Test an empty statement ';'
void main() {
// Start with an empty statement
;
// Fill screen with '*'. Body is an empty statement.
for( char * screen = 0x0400; screen<0x400+1000; (*screen++)='*') ;
// End with two empty statements
; ;
}

View File

@ -0,0 +1,35 @@
// Test an empty statement ';'
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
main: {
.label screen = 2
lda #<$400
sta.z screen
lda #>$400
sta.z screen+1
// Fill screen with '*'. Body is an empty statement.
__b1:
// for( char * screen = 0x0400; screen<0x400+1000; (*screen++)='*')
lda.z screen+1
cmp #>$400+$3e8
bcc __b2
bne !+
lda.z screen
cmp #<$400+$3e8
bcc __b2
!:
// }
rts
__b2:
// (*screen++)='*'
lda #'*'
ldy #0
sta (screen),y
// for( char * screen = 0x0400; screen<0x400+1000; (*screen++)='*')
inc.z screen
bne !+
inc.z screen+1
!:
jmp __b1
}

View File

@ -0,0 +1,16 @@
(void()) main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@2
[1] (byte*) main::screen#2 ← phi( main/(byte*) 1024 main::@2/(byte*) main::screen#1 )
[2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[3] return
to:@return
main::@2: scope:[main] from main::@1
[4] *((byte*) main::screen#2) ← (byte) '*'
[5] (byte*) main::screen#1 ← ++ (byte*) main::screen#2
to:main::@1

300
src/test/ref/stmt-empty.log Normal file
View File

@ -0,0 +1,300 @@
CONTROL FLOW GRAPH SSA
(void()) main()
main: scope:[main] from __start
(byte*) main::screen#0 ← (byte*)(number) $400
to:main::@1
main::@1: scope:[main] from main main::@2
(byte*) main::screen#2 ← phi( main/(byte*) main::screen#0 main::@2/(byte*) main::screen#1 )
(bool~) main::$0 ← (byte*) main::screen#2 < (number) $400+(number) $3e8
if((bool~) main::$0) goto main::@2
to:main::@return
main::@2: scope:[main] from main::@1
(byte*) main::screen#3 ← phi( main::@1/(byte*) main::screen#2 )
*((byte*) main::screen#3) ← (byte) '*'
(byte*) main::screen#1 ← ++ (byte*) main::screen#3
to:main::@1
main::@return: scope:[main] from main::@1
return
to:@return
(void()) __start()
__start: scope:[__start] from
call main
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()
(label) __start::@1
(label) __start::@return
(void()) main()
(bool~) main::$0
(label) main::@1
(label) main::@2
(label) main::@return
(byte*) main::screen
(byte*) main::screen#0
(byte*) main::screen#1
(byte*) main::screen#2
(byte*) main::screen#3
Adding number conversion cast (unumber) $400+$3e8 in (bool~) main::$0 ← (byte*) main::screen#2 < (number) $400+(number) $3e8
Successful SSA optimization PassNAddNumberTypeConversions
Simplifying constant pointer cast (byte*) 1024
Successful SSA optimization PassNCastSimplification
Alias main::screen#2 = main::screen#3
Successful SSA optimization Pass2AliasElimination
Simple Condition (bool~) main::$0 [3] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte*) main::screen#0 = (byte*) 1024
Successful SSA optimization Pass2ConstantIdentification
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
Inlining constant with var siblings (const byte*) main::screen#0
Constant inlined main::screen#0 = (byte*) 1024
Successful SSA optimization Pass2ConstantInlining
Adding NOP phi() at start of main
CALL GRAPH
Created 1 initial phi equivalence classes
Coalesced [6] main::screen#4 ← main::screen#1
Coalesced down to 1 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
(void()) main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@2
[1] (byte*) main::screen#2 ← phi( main/(byte*) 1024 main::@2/(byte*) main::screen#1 )
[2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[3] return
to:@return
main::@2: scope:[main] from main::@1
[4] *((byte*) main::screen#2) ← (byte) '*'
[5] (byte*) main::screen#1 ← ++ (byte*) main::screen#2
to:main::@1
VARIABLE REGISTER WEIGHTS
(void()) main()
(byte*) main::screen
(byte*) main::screen#1 22.0
(byte*) main::screen#2 14.666666666666666
Initial phi equivalence classes
[ main::screen#2 main::screen#1 ]
Complete equivalence classes
[ main::screen#2 main::screen#1 ]
Allocated zp[2]:2 [ main::screen#2 main::screen#1 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test an empty statement ';'
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
main: {
.label screen = 2
// [1] phi from main to main::@1 [phi:main->main::@1]
__b1_from_main:
// [1] phi (byte*) main::screen#2 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z screen
lda #>$400
sta.z screen+1
jmp __b1
// Fill screen with '*'. Body is an empty statement.
// main::@1
__b1:
// [2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2 -- pbuz1_lt_vwuc1_then_la1
lda.z screen+1
cmp #>$400+$3e8
bcc __b2
bne !+
lda.z screen
cmp #<$400+$3e8
bcc __b2
!:
jmp __breturn
// main::@return
__breturn:
// [3] return
rts
// main::@2
__b2:
// [4] *((byte*) main::screen#2) ← (byte) '*' -- _deref_pbuz1=vbuc1
lda #'*'
ldy #0
sta (screen),y
// [5] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1
inc.z screen
bne !+
inc.z screen+1
!:
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
__b1_from___b2:
// [1] phi (byte*) main::screen#2 = (byte*) main::screen#1 [phi:main::@2->main::@1#0] -- register_copy
jmp __b1
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2 [ main::screen#2 ] ( [ main::screen#2 ] { } ) always clobbers reg byte a
Statement [4] *((byte*) main::screen#2) ← (byte) '*' [ main::screen#2 ] ( [ main::screen#2 ] { } ) always clobbers reg byte a reg byte y
Potential registers zp[2]:2 [ main::screen#2 main::screen#1 ] : zp[2]:2 ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 36.67: zp[2]:2 [ main::screen#2 main::screen#1 ]
Uplift Scope []
Uplifting [main] best 596 combination zp[2]:2 [ main::screen#2 main::screen#1 ]
Uplifting [] best 596 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test an empty statement ';'
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
main: {
.label screen = 2
// [1] phi from main to main::@1 [phi:main->main::@1]
__b1_from_main:
// [1] phi (byte*) main::screen#2 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z screen
lda #>$400
sta.z screen+1
jmp __b1
// Fill screen with '*'. Body is an empty statement.
// main::@1
__b1:
// [2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2 -- pbuz1_lt_vwuc1_then_la1
lda.z screen+1
cmp #>$400+$3e8
bcc __b2
bne !+
lda.z screen
cmp #<$400+$3e8
bcc __b2
!:
jmp __breturn
// main::@return
__breturn:
// [3] return
rts
// main::@2
__b2:
// [4] *((byte*) main::screen#2) ← (byte) '*' -- _deref_pbuz1=vbuc1
lda #'*'
ldy #0
sta (screen),y
// [5] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1
inc.z screen
bne !+
inc.z screen+1
!:
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
__b1_from___b2:
// [1] phi (byte*) main::screen#2 = (byte*) main::screen#1 [phi:main::@2->main::@1#0] -- register_copy
jmp __b1
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __b1_from_main:
Removing instruction __breturn:
Removing instruction __b1_from___b2:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@return
(byte*) main::screen
(byte*) main::screen#1 screen zp[2]:2 22.0
(byte*) main::screen#2 screen zp[2]:2 14.666666666666666
zp[2]:2 [ main::screen#2 main::screen#1 ]
FINAL ASSEMBLER
Score: 536
// File Comments
// Test an empty statement ';'
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
main: {
.label screen = 2
// [1] phi from main to main::@1 [phi:main->main::@1]
// [1] phi (byte*) main::screen#2 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z screen
lda #>$400
sta.z screen+1
// Fill screen with '*'. Body is an empty statement.
// main::@1
__b1:
// for( char * screen = 0x0400; screen<0x400+1000; (*screen++)='*')
// [2] if((byte*) main::screen#2<(word)(number) $400+(number) $3e8) goto main::@2 -- pbuz1_lt_vwuc1_then_la1
lda.z screen+1
cmp #>$400+$3e8
bcc __b2
bne !+
lda.z screen
cmp #<$400+$3e8
bcc __b2
!:
// main::@return
// }
// [3] return
rts
// main::@2
__b2:
// (*screen++)='*'
// [4] *((byte*) main::screen#2) ← (byte) '*' -- _deref_pbuz1=vbuc1
lda #'*'
ldy #0
sta (screen),y
// for( char * screen = 0x0400; screen<0x400+1000; (*screen++)='*')
// [5] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1
inc.z screen
bne !+
inc.z screen+1
!:
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
// [1] phi (byte*) main::screen#2 = (byte*) main::screen#1 [phi:main::@2->main::@1#0] -- register_copy
jmp __b1
}
// File Data

View File

@ -0,0 +1,9 @@
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@return
(byte*) main::screen
(byte*) main::screen#1 screen zp[2]:2 22.0
(byte*) main::screen#2 screen zp[2]:2 14.666666666666666
zp[2]:2 [ main::screen#2 main::screen#1 ]