1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-19 08:31:01 +00:00

Added support for array initialization using inline kickasm

This commit is contained in:
Jesper Gravgaard 2019-06-30 23:08:39 +02:00
parent 44c5aa1313
commit c4bcbddcd4
22 changed files with 1213 additions and 716 deletions

View File

@ -0,0 +1,66 @@
package dk.camelot64.kickc.asm;
/** A labelled data array initialized by kickasm code. */
public class AsmDataKickAsm implements AsmLine {
private String label;
private int bytes;
private String kickAsmCode;
private int index;
public AsmDataKickAsm(String label, int bytes, String kickAsmCode) {
this.label = label;
this.bytes = bytes;
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 0;
}
@Override
public String getAsm() {
StringBuilder asm = new StringBuilder();
asm.append(label + ":\n");
asm.append(kickAsmCode);
return asm.toString();
}
/**
* Get the number of source lines in the inline assembler code (for line indexing)
*
* @return The number of source lines
*/
public long getLineCount() {
return kickAsmCode.chars().filter(x -> x == '\n').count() + 2;
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -144,6 +144,16 @@ public class AsmProgram {
addLine(new AsmInlineKickAsm(kickAsmCode, bytes, cycles));
}
/**
* Add data array initialized with inline kick assembler code
* @param label Name of the data
* @param bytes The number of bytes (from array definition)
* @param kickAsmCode Kick Assembler code to initialize the data
*/
public void addDataKickAsm(String label, int bytes, String kickAsmCode) {
addLine(new AsmDataKickAsm(label, bytes, kickAsmCode));
}
/**
* Get the number of bytes the segment occupies in memory.
* Calculated by adding up the bytes of each ASM segment in the program.

View File

@ -231,6 +231,7 @@ public class ProgramValueIterator {
value instanceof ConstantLiteral ||
value instanceof ConstantRef ||
value instanceof StructZero ||
value instanceof ConstantArrayKickAsm ||
value instanceof LabelRef
) {
// No sub values

View File

@ -18,7 +18,7 @@ public class StatementKickAsm extends StatementBase {
/** The absolute address to generate the kick-assembler code at. If null it is generated inline. */
private RValue location;
/** The number of bytes generated by the kick-assembler code. */
/** The number of bytes generated by the kick-assembler code. */
private RValue bytes;
/** The number of cycles used by the generated kick-assembler code. */

View File

@ -56,6 +56,8 @@ public class SymbolTypeInference {
}
} else if(rValue instanceof ConstantArrayList) {
return new SymbolTypeArray(((ConstantArrayList) rValue).getElementType());
} else if(rValue instanceof ConstantArrayKickAsm) {
return new SymbolTypeArray(((ConstantArrayKickAsm) rValue).getElementType());
} else if(rValue instanceof ArrayFilled) {
return new SymbolTypeArray(((ArrayFilled) rValue).getElementType(), ((ArrayFilled) rValue).getSize());
} else if(rValue instanceof ConstantArrayFilled) {

View File

@ -0,0 +1,12 @@
package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.types.SymbolType;
/**
* An array initialized with constant values.
*/
public interface ConstantArray extends ConstantValue {
SymbolType getElementType();
}

View File

@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.types.SymbolTypeArray;
/**
* An zero-filled constant array. The array is allocated in the code memory (as a .fill() ).
*/
public class ConstantArrayFilled implements ConstantValue {
public class ConstantArrayFilled implements ConstantArray {
private ConstantValue size;

View File

@ -0,0 +1,63 @@
package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.ConstantNotLiteral;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeArray;
import java.util.List;
/**
* An array initialized using inline kick-assembler. The array is allocated in the code memory (eg. as a set of .byte's ).
*/
public class ConstantArrayKickAsm implements ConstantArray {
/** Type of the array elements. */
private SymbolType elementType;
/** KickAssembler code generating the data. */
private String kickAsmCode;
public ConstantArrayKickAsm(SymbolType elementType, String kickAsmCode) {
this.elementType = elementType;
this.kickAsmCode = kickAsmCode;
}
@Override
public SymbolType getType(ProgramScope scope) {
return new SymbolTypeArray(elementType);
}
public SymbolType getElementType() {
return elementType;
}
public String getKickAsmCode() {
return kickAsmCode;
}
public void setKickAsmCode(String kickAsmCode) {
this.kickAsmCode = kickAsmCode;
}
@Override
public ConstantLiteral calculateLiteral(ProgramScope scope) {
throw new ConstantNotLiteral("Cannot calculate literal array");
}
@Override
public String toString() {
return toString(null);
}
@Override
public String toString(Program program) {
StringBuilder txt = new StringBuilder();
txt.append("kickasm {{ ");
txt.append(kickAsmCode);
txt.append(" }}");
return txt.toString();
}
}

View File

@ -10,7 +10,7 @@ import java.util.List;
/**
* An array of constants. The array is allocated in the code memory (eg. as a set of .byte's ).
*/
public class ConstantArrayList implements ConstantValue {
public class ConstantArrayList implements ConstantArray {
private List<ConstantValue> list;

View File

@ -51,7 +51,7 @@ declVariableList
declVariableInit
: NAME ('=' expr)? #declVariableInitExpr
| NAME ('=' declKasm)? #declVariableInitKasm
| NAME '=' declKasm #declVariableInitKasm
;
declFunction

File diff suppressed because it is too large Load Diff

View File

@ -247,6 +247,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
sequence.addStatement(statementKickAsm);
return statementKickAsm;
}
return null;
}
@ -523,7 +524,37 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public Object visitDeclVariableInitKasm(KickCParser.DeclVariableInitKasmContext ctx) {
throw new InternalError("Not implemented");
String varName = ctx.NAME().getText();
VariableUnversioned lValue = visitDeclVariableInit(varName, ctx);
SymbolType type = this.declVarType;
List<Comment> comments = this.declVarComments;
if(!(type instanceof SymbolTypeArray)) {
throw new CompileError("KickAsm initializers only supported for arrays " + type.getTypeName(), new StatementSource(ctx));
}
// Add KickAsm statement
StatementKickAsm kasm = (StatementKickAsm) this.visit(ctx.declKasm());
if(kasm.getUses().size()>0) {
throw new CompileError("KickAsm initializers does not support 'uses' directive.", new StatementSource(ctx));
}
if(kasm.getLocation()!=null) {
throw new CompileError("KickAsm initializers does not support 'location' directive.", new StatementSource(ctx));
}
if(kasm.getCycles()!=null) {
throw new CompileError("KickAsm initializers does not support 'cycles' directive.", new StatementSource(ctx));
}
if(kasm.getBytes()!=null) {
throw new CompileError("KickAsm initializers does not support 'bytes' directive.", new StatementSource(ctx));
}
if(kasm.getDeclaredClobber()!=null) {
throw new CompileError("KickAsm initializers does not support 'clobbers' directive.", new StatementSource(ctx));
}
ConstantArrayKickAsm constantArrayKickAsm = new ConstantArrayKickAsm(((SymbolTypeArray) type).getElementType(), kasm.getKickAsmCode());
// Remove the KickAsm statement
sequence.getStatements().remove(sequence.getStatements().size()-1);
// Add an initializer statement instead
Statement stmt = new StatementAssignment(lValue.getRef(), constantArrayKickAsm, new StatementSource(ctx), ensureUnusedComments(comments));
sequence.addStatement(stmt);
return null;
}
private VariableUnversioned visitDeclVariableInit(String varName, KickCParser.DeclVariableInitContext ctx) {

View File

@ -93,7 +93,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization {
Collection<ConstantVar> allConstants = getProgram().getScope().getAllConstants(true);
for(ConstantVar constant : allConstants) {
if(constant.getRef().isIntermediate()) {
if(!(constant.getType().equals(SymbolType.STRING)) && !(constant.getValue() instanceof ConstantArrayList) && !(constant.getValue() instanceof ConstantArrayFilled)) {
if(!(constant.getType().equals(SymbolType.STRING)) && !(constant.getValue() instanceof ConstantArray)) {
unnamed.put(constant.getRef(), constant.getValue());
}
}
@ -150,7 +150,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization {
break;
} else if(symbol instanceof ConstantVar) {
ConstantValue otherValue = ((ConstantVar) symbol).getValue();
if(!otherValue.equals(value) && !(value instanceof ConstantString) && !(value instanceof ConstantArrayList) && !(value instanceof ConstantArrayFilled) && !(otherValue instanceof ConstantRef)) {
if(!otherValue.equals(value) && !(value instanceof ConstantString) && !(value instanceof ConstantArray) && !(otherValue instanceof ConstantRef)) {
aliases.put(constant.getRef(), value);
getLog().append("Inlining constant with different constant siblings " + constant);
break;

View File

@ -3,6 +3,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.asm.*;
import dk.camelot64.kickc.fragment.*;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
@ -300,9 +301,7 @@ public class Pass4CodeGeneration {
private boolean hasData(ConstantVar constantVar) {
ConstantValue constantValue = constantVar.getValue();
if(constantValue instanceof ConstantArrayList) {
return true;
} else if(constantValue instanceof ConstantArrayFilled) {
if(constantValue instanceof ConstantArray) {
return true;
} else {
try {
@ -471,8 +470,24 @@ public class Pass4CodeGeneration {
asm.addDataFilled(asmName.replace("#", "_").replace("$", "_"), AsmDataNumeric.Type.WORD, asmSize, size, "0");
added.add(asmName);
} else {
throw new RuntimeException("Unhandled constant array element type " + constantArrayFilled.toString(program));
throw new InternalError("Unhandled constant array element type " + constantArrayFilled.toString(program));
}
} else if(constantVar.getValue() instanceof ConstantArrayKickAsm) {
ConstantArrayKickAsm kickAsm = (ConstantArrayKickAsm) constantVar.getValue();
SymbolType type = constantVar.getType();
// default - larger then 256
int bytes = 1023;
if(type instanceof SymbolTypeArray) {
SymbolType elementType = ((SymbolTypeArray) type).getElementType();
RValue size = ((SymbolTypeArray) type).getSize();
if(size instanceof ConstantValue) {
ConstantLiteral sizeLiteral = ((ConstantValue) size).calculateLiteral(getScope());
if(sizeLiteral instanceof ConstantInteger) {
bytes = (int)sizeLiteral.getValue() * elementType.getSizeBytes();
}
}
}
asm.addDataKickAsm(asmName.replace("#", "_").replace("$", "_"), bytes, kickAsm.getKickAsmCode());
} else {
try {
ConstantLiteral literal = constantVar.getValue().calculateLiteral(getScope());

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 || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding) {
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmDataKickAsm) {
currentLabel = null;
} else if(line instanceof AsmInstruction) {
if(currentLabel != null) {

View File

@ -1,9 +1,6 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.asm.AsmComment;
import dk.camelot64.kickc.asm.AsmInlineKickAsm;
import dk.camelot64.kickc.asm.AsmLine;
import dk.camelot64.kickc.asm.AsmSegment;
import dk.camelot64.kickc.asm.*;
import dk.camelot64.kickc.model.Program;
/**
@ -26,6 +23,10 @@ public class Pass5ReindexAsmLines extends Pass5AsmOptimization {
asmLine.setIndex(nextIndex);
AsmInlineKickAsm inlineKickAsm = (AsmInlineKickAsm) asmLine;
nextIndex += inlineKickAsm.getLineCount();
} else if(asmLine instanceof AsmDataKickAsm) {
asmLine.setIndex(nextIndex);
AsmDataKickAsm inlineKickAsm = (AsmDataKickAsm) asmLine;
nextIndex += inlineKickAsm.getLineCount();
} else {
asmLine.setIndex(nextIndex++);
}

View File

@ -35,6 +35,11 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testArraysInitKasm0() throws IOException, URISyntaxException {
compileAndCompare("arrays-init-kasm-0", log());
}
@Test
public void testScreenCenterAngle() throws IOException, URISyntaxException {
compileAndCompare("screen-center-angle");

View File

@ -0,0 +1,13 @@
// Test initializing array using KickAssembler
// Sinus table
byte[] SINTAB = kickasm {{
.fill 256, 128 + 128*sin(i*2*PI/256)
}};
byte* SCREEN = 0x400;
void main() {
SCREEN[0] = SINTAB[0];
}

View File

@ -0,0 +1,14 @@
// Test initializing array using KickAssembler
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
main: {
lda SINTAB
sta SCREEN
rts
}
// Sinus table
SINTAB:
.fill 256, 128 + 128*sin(i*2*PI/256)

View File

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

View File

@ -0,0 +1,247 @@
Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400
Identified constant variable (byte*) SCREEN
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte[]) SINTAB#0 ← kickasm {{ .fill 256, 128 + 128*sin(i*2*PI/256)
}}
(byte*) SCREEN#0 ← ((byte*)) (number) $400
to:@1
main: scope:[main] from @1
*((byte*) SCREEN#0 + (number) 0) ← *((byte[]) SINTAB#0 + (number) 0)
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
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
(byte*) SCREEN
(byte*) SCREEN#0
(byte[]) SINTAB
(byte[]) SINTAB#0
(void()) main()
(label) main::@return
Adding number conversion cast (unumber) 0 in *((byte*) SCREEN#0 + (number) 0) ← *((byte[]) SINTAB#0 + (number) 0)
Adding number conversion cast (unumber) 0 in *((byte*) SCREEN#0 + (number) 0) ← *((byte[]) SINTAB#0 + (unumber)(number) 0)
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 0
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Constant (const byte[]) SINTAB#0 = kickasm {{ .fill 256, 128 + 128*sin(i*2*PI/256)
}}
Constant (const byte*) SCREEN#0 = (byte*) 1024
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SINTAB#0 in [2] *((const byte*) SCREEN#0 + (byte) 0) ← *((const byte[]) SINTAB#0 + (byte) 0)
Simplifying expression containing zero SCREEN#0 in [2] *((const byte*) SCREEN#0 + (byte) 0) ← *((const byte[]) SINTAB#0)
Successful SSA optimization PassNSimplifyExpressionWithZero
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Culled Empty Block (label) @2
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] *((const byte*) SCREEN#0) ← *((const byte[]) SINTAB#0)
to:main::@return
main::@return: scope:[main] from main
[5] return
to:@return
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(byte[]) SINTAB
(void()) main()
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
//SEG0 File Comments
// Test initializing array using KickAssembler
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
.label SCREEN = $400
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
//SEG10 [4] *((const byte*) SCREEN#0) ← *((const byte[]) SINTAB#0) -- _deref_pbuc1=_deref_pbuc2
lda SINTAB
sta SCREEN
jmp breturn
//SEG11 main::@return
breturn:
//SEG12 [5] return
rts
}
//SEG13 File Data
// Sinus table
SINTAB:
.fill 256, 128 + 128*sin(i*2*PI/256)
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const byte*) SCREEN#0) ← *((const byte[]) SINTAB#0) [ ] ( main:2 [ ] ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 29 combination
Uplifting [] best 29 combination
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 File Comments
// Test initializing array using KickAssembler
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
.label SCREEN = $400
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
//SEG10 [4] *((const byte*) SCREEN#0) ← *((const byte[]) SINTAB#0) -- _deref_pbuc1=_deref_pbuc2
lda SINTAB
sta SCREEN
jmp breturn
//SEG11 main::@return
breturn:
//SEG12 [5] return
rts
}
//SEG13 File Data
// Sinus table
SINTAB:
.fill 256, 128 + 128*sin(i*2*PI/256)
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(byte[]) SINTAB
(const byte[]) SINTAB#0 SINTAB = kickasm {{ .fill 256, 128 + 128*sin(i*2*PI/256)
}}
(void()) main()
(label) main::@return
FINAL ASSEMBLER
Score: 14
//SEG0 File Comments
// Test initializing array using KickAssembler
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG2 Global Constants & labels
.label SCREEN = $400
//SEG3 @begin
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG5 @1
//SEG6 [2] call main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
//SEG8 @end
//SEG9 main
main: {
//SEG10 [4] *((const byte*) SCREEN#0) ← *((const byte[]) SINTAB#0) -- _deref_pbuc1=_deref_pbuc2
lda SINTAB
sta SCREEN
//SEG11 main::@return
//SEG12 [5] return
rts
}
//SEG13 File Data
// Sinus table
SINTAB:
.fill 256, 128 + 128*sin(i*2*PI/256)

View File

@ -0,0 +1,11 @@
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(byte[]) SINTAB
(const byte[]) SINTAB#0 SINTAB = kickasm {{ .fill 256, 128 + 128*sin(i*2*PI/256)
}}
(void()) main()
(label) main::@return