1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-19 00:29:29 +00:00

Implemented support for inline KickAssembler code

This commit is contained in:
Jesper Gravgaard 2018-07-07 13:55:15 +02:00
parent 198c941d85
commit a82c5cf5b1
16 changed files with 485 additions and 5 deletions

View File

@ -0,0 +1,66 @@
package dk.camelot64.kickc.asm;
/** Inlined KickAssembler code.
* If no cycles/byte size is specified it defaults to 100/100.
* */
public class AsmInlineKickAsm implements AsmLine {
private String kickAsmCode;
private int index;
private int bytes = 100;
private double cycles = 100;
public AsmInlineKickAsm(String kickAsmCode) {
this.kickAsmCode= kickAsmCode;
}
public String getKickAsmCode() {
return kickAsmCode;
}
public void setKickAsmCode(String kickAsmCode) {
this.kickAsmCode = kickAsmCode;
}
@Override
public int getLineBytes() {
return bytes;
}
@Override
public double getLineCycles() {
return cycles;
}
public void setBytes(int bytes) {
this.bytes = bytes;
}
public void setCycles(double cycles) {
this.cycles = cycles;
}
@Override
public String getAsm() {
return kickAsmCode;
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
@Override
public String toString() {
return getAsm();
}
}

View File

@ -131,6 +131,13 @@ public class AsmProgram {
addLine(new AsmDataAlignment(alignment));
}
/**
* Add inlines kick assembler code
* @param kickAsmCode The kickassembler code
*/
public void addInlinedKickAsm(String kickAsmCode) {
addLine(new AsmInlineKickAsm(kickAsmCode));
}
/**
* Get the number of bytes the segment occupies in memory.

View File

@ -175,7 +175,7 @@ public class AsmSegment {
if(printState.isComments()) {
out.append(printState.getIndent()).append("//SEG").append(getIndex());
if(source != null) {
out.append(" ").append(source);
out.append(" ").append(source.replace('\r', ' ').replace('\n', ' '));
}
if(phiTransitionId != null) {
out.append(" [").append(phiTransitionId);

View File

@ -43,6 +43,8 @@ public class ControlFlowGraphBaseVisitor<T> {
return visitProcedureEnd((StatementProcedureEnd) statement);
} else if(statement instanceof StatementAsm) {
return visitAsm((StatementAsm) statement);
} else if(statement instanceof StatementKickAsm) {
return visitKickAsm((StatementKickAsm) statement);
} else {
throw new RuntimeException("Unhandled statement type " + statement);
}
@ -87,4 +89,8 @@ public class ControlFlowGraphBaseVisitor<T> {
public T visitAsm(StatementAsm asm) {
return null;
}
public T visitKickAsm(StatementKickAsm asm) {
return null;
}
}

View File

@ -193,4 +193,10 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
public Object visitAsm(StatementAsm asm) {
return new StatementAsm(asm.getAsmLines(), asm.getSource());
}
@Override
public Object visitKickAsm(StatementKickAsm kasm) {
return new StatementKickAsm(kasm.getKickAsmCode(), kasm.getSource());
}
}

View File

@ -0,0 +1,29 @@
package dk.camelot64.kickc.model.statements;
import dk.camelot64.kickc.model.Program;
/** Inline KickAssembler code */
public class StatementKickAsm extends StatementBase {
/** KickAssembler code. */
private String kickAsmCode;
public StatementKickAsm(String kickAsmCode, StatementSource source) {
super(null, source);
this.kickAsmCode = kickAsmCode;
}
@Override
public String toString(Program program, boolean aliveInfo) {
StringBuilder txt = new StringBuilder();
txt.append("kickasm {{ ");
txt.append(kickAsmCode);
txt.append(" }}");
return txt.toString();
}
public String getKickAsmCode() {
return kickAsmCode;
}
}

View File

@ -19,6 +19,8 @@ import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Generates program SSA form by visiting the ANTLR4 parse tree
@ -139,6 +141,18 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
return param;
}
@Override
public Object visitDeclKasm(KickCParser.DeclKasmContext ctx) {
String kasm = ctx.KICKASM().getText();
Pattern p = Pattern.compile("\\{\\{[\\s]*(.*)[\\s]*\\}\\}", Pattern.DOTALL);
Matcher m = p.matcher(kasm);
if(m.find()) {
String kickAsmCode = m.group(1);
sequence.addStatement(new StatementKickAsm(kickAsmCode, new StatementSource(ctx)));
}
return null;
}
@Override
public Object visitDeclVariable(KickCParser.DeclVariableContext ctx) {
SymbolType type = (SymbolType) visit(ctx.typeDecl());
@ -166,8 +180,6 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
return null;
}
/**
* Add declared directives to an lValue (typically a variable).
*
@ -224,7 +236,6 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
@Override
public Directive visitDirectiveConst(KickCParser.DirectiveConstContext ctx) {
return new DirectiveConst();

View File

@ -386,6 +386,9 @@ public class Pass4CodeGeneration {
HashMap<String, Value> bindings = new HashMap<>();
AsmFragmentInstance asmFragmentInstance = new AsmFragmentInstance(program, "inline", block.getScope(), new AsmFragmentTemplate(statementAsm.getAsmLines()), bindings);
asmFragmentInstance.generate(asm);
} else if(statement instanceof StatementKickAsm) {
StatementKickAsm statementKasm = (StatementKickAsm) statement;
asm.addInlinedKickAsm(statementKasm.getKickAsmCode());
} else {
throw new RuntimeException("Statement not supported " + statement);
}

View File

@ -34,7 +34,7 @@ public class Pass5DoubleJumpElimination extends Pass5AsmOptimization {
currentLabel = ((AsmLabel) line).getLabel();
} else if(line instanceof AsmComment || line instanceof AsmConstant || line instanceof AsmLabelDecl) {
// ignore
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc) {
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm) {
currentLabel = null;
} else if(line instanceof AsmInstruction) {
if(currentLabel != null) {

View File

@ -346,6 +346,8 @@ public class PassNVariableReferenceInfos extends Pass2Base {
referenced.addAll(getReferenced(statementReturn.getValue()));
} else if(statement instanceof StatementAsm) {
// No references in ASM atm.
} else if(statement instanceof StatementKickAsm) {
// No references in ASM atm.
} else {
throw new RuntimeException("Unknown statement type " + statement);
}

View File

@ -44,6 +44,11 @@ public class TestPrograms {
AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false);
}
@Test
public void testKasm() throws IOException, URISyntaxException {
compileAndCompare("test-kasm");
}
@Test
public void testLineAnim() throws IOException, URISyntaxException {
compileAndCompare("line-anim");

View File

@ -0,0 +1,13 @@
// Test inline KickAssembler code
void main() {
while(true) {
kickasm {{
inc $d020
}}
}
}
kickasm {{
.byte 1, 2, 3
}}

View File

@ -0,0 +1,12 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.byte 1, 2, 3
jsr main
main: {
b2:
inc $d020
jmp b2
}

View File

@ -0,0 +1,17 @@
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
kickasm {{ .byte 1, 2, 3
}}
[2] call main [ ] ( )
to:@end
@end: scope:[] from @1
[3] phi() [ ] ( )
main: scope:[main] from @1
[4] phi() [ ] ( main:2 [ ] )
to:main::@2
main::@2: scope:[main] from main main::@2
kickasm {{ inc $d020
}}
to:main::@2

View File

@ -0,0 +1,297 @@
PARSING src/test/java/dk/camelot64/kickc/test/kc/test-kasm.kc
// Test inline KickAssembler code
void main() {
while(true) {
kickasm {{
inc $d020
}}
}
}
kickasm {{
.byte 1, 2, 3
}}
SYMBOLS
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@5
(label) main::@6
(label) main::@return
INITIAL CONTROL FLOW GRAPH
@begin: scope:[] from
to:@1
main: scope:[main] from
to:main::@1
main::@1: scope:[main] from main main::@2
if(true) goto main::@2
to:main::@4
main::@2: scope:[main] from main::@1 main::@5
kickasm {{ inc $d020
}}
to:main::@1
main::@4: scope:[main] from main::@1
to:main::@3
main::@3: scope:[main] from main::@4 main::@6
to:main::@return
main::@5: scope:[main] from
to:main::@2
main::@6: scope:[main] from
to:main::@3
main::@return: scope:[main] from main::@3
return
to:@return
@1: scope:[] from @begin
kickasm {{ .byte 1, 2, 3
}}
call main
to:@end
@end: scope:[] from @1
Removing empty block main::@4
Removing empty block main::@3
Removing empty block main::@5
Removing empty block main::@6
PROCEDURE MODIFY VARIABLE ANALYSIS
Completing Phi functions...
CONTROL FLOW GRAPH SSA WITH ASSIGNMENT CALL & RETURN
@begin: scope:[] from
to:@1
main: scope:[main] from @1
to:main::@1
main::@1: scope:[main] from main main::@2
if(true) goto main::@2
to:main::@return
main::@2: scope:[main] from main::@1
kickasm {{ inc $d020
}}
to:main::@1
main::@return: scope:[main] from main::@1
return
to:@return
@1: scope:[] from @begin
kickasm {{ .byte 1, 2, 3
}}
call main
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
SYMBOL TABLE SSA
(label) @1
(label) @2
(label) @begin
(label) @end
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@return
OPTIMIZING CONTROL FLOW GRAPH
Culled Empty Block (label) @2
Succesful SSA optimization Pass2CullEmptyBlocks
if() condition always true - replacing block destination if(true) goto main::@2
Succesful SSA optimization Pass2ConstantIfs
Removing unused block main::@return
Succesful SSA optimization Pass2EliminateUnusedBlocks
Culled Empty Block (label) main::@1
Succesful SSA optimization Pass2CullEmptyBlocks
OPTIMIZING CONTROL FLOW GRAPH
Block Sequence Planned @begin @1 @end main main::@2
Block Sequence Planned @begin @1 @end main main::@2
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
CALL GRAPH
Calls in [] to main:2
Propagating live ranges...
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Block Sequence Planned @begin @1 @end main main::@2
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Propagating live ranges...
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
kickasm {{ .byte 1, 2, 3
}}
[2] call main [ ] ( )
to:@end
@end: scope:[] from @1
[3] phi() [ ] ( )
main: scope:[main] from @1
[4] phi() [ ] ( main:2 [ ] )
to:main::@2
main::@2: scope:[main] from main main::@2
kickasm {{ inc $d020
}}
to:main::@2
DOMINATORS
@begin dominated by @begin
@1 dominated by @1 @begin
@end dominated by @1 @begin @end
main dominated by @1 @begin main
main::@2 dominated by @1 @begin main::@2 main
NATURAL LOOPS
Found back edge: Loop head: main::@2 tails: main::@2 blocks: null
Populated: Loop head: main::@2 tails: main::@2 blocks: main::@2
Loop head: main::@2 tails: main::@2 blocks: main::@2
NATURAL LOOPS WITH DEPTH
Found 0 loops in scope []
Found 1 loops in scope [main]
Loop head: main::@2 tails: main::@2 blocks: main::@2
Loop head: main::@2 tails: main::@2 blocks: main::@2 depth: 1
VARIABLE REGISTER WEIGHTS
(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
//SEG2 @begin
bbegin:
jmp b1
//SEG3 @1
b1:
//SEG4 kickasm {{ .byte 1, 2, 3 }}
.byte 1, 2, 3
//SEG5 [2] call main [ ] ( )
//SEG6 [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
jmp b2
//SEG10 main::@2
b2:
//SEG11 kickasm {{ inc $d020 }}
inc $d020
jmp b2
}
REGISTER UPLIFT POTENTIAL REGISTERS
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 1199 combination
Uplifting [] best 1199 combination
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
//SEG2 @begin
bbegin:
jmp b1
//SEG3 @1
b1:
//SEG4 kickasm {{ .byte 1, 2, 3 }}
.byte 1, 2, 3
//SEG5 [2] call main [ ] ( )
//SEG6 [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
jmp b2
//SEG10 main::@2
b2:
//SEG11 kickasm {{ inc $d020 }}
inc $d020
jmp b2
}
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp b2
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction bbegin:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@2
FINAL ASSEMBLER
Score: 1136
//SEG0 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG1 Global Constants & labels
//SEG2 @begin
//SEG3 @1
//SEG4 kickasm {{ .byte 1, 2, 3 }}
.byte 1, 2, 3
//SEG5 [2] call main [ ] ( )
//SEG6 [4] phi from @1 to main [phi:@1->main]
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
//SEG8 @end
//SEG9 main
main: {
//SEG10 main::@2
b2:
//SEG11 kickasm {{ inc $d020 }}
inc $d020
jmp b2
}

View File

@ -0,0 +1,6 @@
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@2