mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-03 12:07:26 +00:00
Added support for ASM .byte directives through array initializers.
This commit is contained in:
parent
feba0565b4
commit
1a649008b9
@ -1,6 +1,10 @@
|
||||
Known Problems
|
||||
- In immemarray.kc the if(++j==8) is broken because the optimizer culls the empty block main::@3 (because the value is constant). In the end the phi()-function in main::@2 has two handlers for min::@1 phi( main::@1/(byte) main::j#1 main::@1/(const byte) main::j#2 )
|
||||
|
||||
Features
|
||||
- Add string constants
|
||||
- Add support for expressions in data initializers.
|
||||
- Add support for empty / filled byte data arrays.
|
||||
- Move the main code into a main() function, and disallow code outside functions. The main function per default has no parameters and exits with RTS.
|
||||
- Improve locality of block sequence for if(cond) { stmt1; } else { stmt2; } to if(!cond) goto @else stmt1; jmp @end; @else: stmt2; @end:
|
||||
- Optimize if/else by swapping if & else if cond is easier to evaluate than !cond.
|
||||
@ -10,7 +14,6 @@ Features
|
||||
- Add Fixed Point number types fixed[8.8], fixed[16.8] - maybe even fixed[24.4]
|
||||
- Add imports
|
||||
- Add structs
|
||||
- Add possibility of declaring in-program data - just like .byte/.fill in KickAss.
|
||||
- Let { stmt } introduce a new anonymous scope. (But not when optimizing)
|
||||
- Add preprocessing / find a way to allow some functions to run at compile time
|
||||
- Implement inline compilation of functions (and a mechanism for choosing which methods / calls to inline)
|
||||
@ -116,3 +119,4 @@ Done
|
||||
+ In loopnest.asm x&y are used in both loops - the outer x&y are clobbered by the inner loop.
|
||||
+ In voronoi.asm in render() x is clobbered during call to findcol().
|
||||
+ Optimize getAliveEffective() to improve speed
|
||||
+ Add possibility of declaring in-program data - just like .byte/.fill in KickAss.
|
||||
|
73
src/main/java/dk/camelot64/kickc/asm/AsmData.java
Normal file
73
src/main/java/dk/camelot64/kickc/asm/AsmData.java
Normal file
@ -0,0 +1,73 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** A labelled numeric data directive. */
|
||||
public class AsmData implements AsmLine {
|
||||
|
||||
private String label;
|
||||
private Type type;
|
||||
private List<String> values;
|
||||
private int index;
|
||||
|
||||
public static enum Type {
|
||||
BYTE("byte", 1),
|
||||
WORD("word", 2),
|
||||
DWORD("dword", 4);
|
||||
|
||||
public final String asm;
|
||||
public final int bytes;
|
||||
|
||||
Type(String asm, int bytes) {
|
||||
this.asm = asm;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AsmData(String label, Type type, List<String> values) {
|
||||
this.label = label;
|
||||
this.type = type;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public int getElementBytes() {
|
||||
return type.bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return values.size()*getElementBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuilder asm = new StringBuilder();
|
||||
asm.append(label+": ");
|
||||
asm.append("."+type.asm+" ");
|
||||
boolean first = true;
|
||||
for (String value : values) {
|
||||
if(!first)
|
||||
asm.append(", ");
|
||||
first = false;
|
||||
asm.append(value);
|
||||
}
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
}
|
@ -85,7 +85,15 @@ public class AsmProgram {
|
||||
addLine(new AsmConstant(name, value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a BYTE/WORD/DWORD data declaration tot the ASM
|
||||
* @param label The label of the data
|
||||
* @param type The type of the data
|
||||
* @param asmElements The value of the elements
|
||||
*/
|
||||
public void addData(String label, AsmData.Type type, List<String> asmElements) {
|
||||
addLine(new AsmData(label, type, asmElements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes the segment occupies in memory.
|
||||
@ -175,19 +183,4 @@ public class AsmProgram {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all segments representing a specific ICL statement
|
||||
* @param statementIndex The statement index
|
||||
* @return The ASM segments representing the statement
|
||||
* */
|
||||
public Collection<AsmSegment> getSegmentsByStatementIndex(int statementIndex) {
|
||||
List<AsmSegment> statementSegments = new ArrayList<>();
|
||||
for (AsmSegment segment : segments) {
|
||||
if(segment.getStatementIdx()!=null && segment.getStatementIdx()==statementIndex) {
|
||||
statementSegments.add(segment);
|
||||
}
|
||||
}
|
||||
return statementSegments;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ public class AsmSegment {
|
||||
printState.decIndent();
|
||||
}
|
||||
out.append(printState.getIndent());
|
||||
if (line instanceof AsmComment || line instanceof AsmInstruction || line instanceof AsmLabelDecl || line instanceof AsmConstant) {
|
||||
if (line instanceof AsmComment || line instanceof AsmInstruction || line instanceof AsmLabelDecl || line instanceof AsmConstant || line instanceof AsmData ) {
|
||||
out.append(" ");
|
||||
}
|
||||
out.append(line.getAsm() + "\n");
|
||||
|
53
src/main/java/dk/camelot64/kickc/model/ConstantArray.java
Normal file
53
src/main/java/dk/camelot64/kickc/model/ConstantArray.java
Normal file
@ -0,0 +1,53 @@
|
||||
package dk.camelot64.kickc.model;
|
||||
|
||||
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 ConstantArray implements ConstantValue {
|
||||
|
||||
private List<ConstantValue> list;
|
||||
|
||||
private SymbolType elementType;
|
||||
|
||||
public ConstantArray(List<ConstantValue> list, SymbolType elementType) {
|
||||
this.list = list;
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolType getType(ProgramScope scope) {
|
||||
return new SymbolTypeArray(elementType);
|
||||
}
|
||||
|
||||
public SymbolType getElementType() {
|
||||
return elementType;
|
||||
}
|
||||
|
||||
public List<ConstantValue> getElements() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Program program) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
boolean first = true;
|
||||
out.append("{ ");
|
||||
for (ConstantValue constantValue : list) {
|
||||
if (!first) {
|
||||
out.append(", ");
|
||||
}
|
||||
first = false;
|
||||
out.append(constantValue.toString(program));
|
||||
}
|
||||
out.append(" }");
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
}
|
34
src/main/java/dk/camelot64/kickc/model/ValueArray.java
Normal file
34
src/main/java/dk/camelot64/kickc/model/ValueArray.java
Normal file
@ -0,0 +1,34 @@
|
||||
package dk.camelot64.kickc.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** An array of sub-values. Only used for variable initializers. Compilation execution will typically resolve it into a constant array before ASM-generation. */
|
||||
public class ValueArray implements RValue {
|
||||
|
||||
private List<RValue> list;
|
||||
|
||||
public ValueArray(List<RValue> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public List<RValue> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Program program) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
boolean first = true;
|
||||
out.append("{ ");
|
||||
for (RValue constantValue : list) {
|
||||
if (!first) {
|
||||
out.append(", ");
|
||||
}
|
||||
first = false;
|
||||
out.append(constantValue.toString(program));
|
||||
}
|
||||
out.append(" }");
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
}
|
@ -397,7 +397,12 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
|
||||
@Override
|
||||
public RValue visitInitList(KickCParser.InitListContext ctx) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
List<RValue> initValues = new ArrayList<>();
|
||||
for (KickCParser.InitializerContext initializer : ctx.initializer()) {
|
||||
RValue rValue = (RValue) visit(initializer);
|
||||
initValues.add(rValue);
|
||||
}
|
||||
return new ValueArray(initValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,10 +2,14 @@ package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Compiler Pass propagating constants in expressions eliminating constant variables */
|
||||
/**
|
||||
* Compiler Pass propagating constants in expressions eliminating constant variables
|
||||
*/
|
||||
public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
||||
|
||||
public Pass2ConstantIdentification(Program program) {
|
||||
@ -14,6 +18,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
||||
|
||||
/**
|
||||
* Propagate constants, replacing variables with constants where possible.
|
||||
*
|
||||
* @return true optimization was performed. false if no optimization was possible.
|
||||
*/
|
||||
@Override
|
||||
@ -33,7 +38,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
||||
constScope.remove(variable);
|
||||
constScope.add(constantVar);
|
||||
constAliases.put(constRef, constantVar.getRef());
|
||||
getLog().append("Constant " + constantVar.toString(getProgram()) + " = "+constantVar.getValue());
|
||||
getLog().append("Constant " + constantVar.toString(getProgram()) + " = " + constantVar.getValue());
|
||||
}
|
||||
// Remove assignments to constants in the code
|
||||
removeAssignments(constants.keySet());
|
||||
@ -44,6 +49,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
||||
|
||||
/**
|
||||
* Find variables that have constant values.
|
||||
*
|
||||
* @return Map from Variable to the Constant value
|
||||
*/
|
||||
private Map<VariableRef, ConstantValue> findConstantVariables() {
|
||||
@ -74,7 +80,36 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
||||
if (constant != null) {
|
||||
constants.put(variable, constant);
|
||||
}
|
||||
} else if (assignment.getrValue2() instanceof ValueArray && assignment.getOperator() == null && assignment.getrValue1() == null) {
|
||||
ValueArray valueArray = (ValueArray) assignment.getrValue2();
|
||||
List<RValue> values = valueArray.getList();
|
||||
boolean allConstant = true;
|
||||
SymbolType elementType = null;
|
||||
List<ConstantValue> elements = new ArrayList<>();
|
||||
for (RValue value : values) {
|
||||
if (value instanceof ConstantValue) {
|
||||
ConstantValue constantValue = (ConstantValue) value;
|
||||
SymbolType type = constantValue.getType(getSymbols());
|
||||
if (elementType == null) {
|
||||
elementType = type;
|
||||
} else {
|
||||
if (!elementType.equals(type)) {
|
||||
throw new RuntimeException("Array type mismatch " + elementType + "!=" + type + " " + valueArray.toString(getProgram()));
|
||||
}
|
||||
}
|
||||
elements.add(constantValue);
|
||||
} else {
|
||||
allConstant = false;
|
||||
elementType = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allConstant && elementType != null) {
|
||||
ConstantValue constant = new ConstantArray(elements, elementType);
|
||||
constants.put(variable, constant);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public class Pass4CodeGeneration {
|
||||
asm.startSegment(null, "Global Constants & labels");
|
||||
addConstants(asm, currentScope);
|
||||
addZpLabels(asm, currentScope);
|
||||
addData(asm, currentScope);
|
||||
for (ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
if (!block.getScope().equals(currentScope)) {
|
||||
if (!ScopeRef.ROOT.equals(currentScope)) {
|
||||
@ -44,6 +45,7 @@ public class Pass4CodeGeneration {
|
||||
// Add all ZP labels for the scope
|
||||
addConstants(asm, currentScope);
|
||||
addZpLabels(asm, currentScope);
|
||||
addData(asm, currentScope);
|
||||
}
|
||||
// Generate entry points (if needed)
|
||||
genBlockEntryPoints(asm, block);
|
||||
@ -90,14 +92,49 @@ public class Pass4CodeGeneration {
|
||||
Collection<ConstantVar> scopeConstants = scope.getAllConstants(false);
|
||||
Set<String> added = new LinkedHashSet<>();
|
||||
for (ConstantVar constantVar : scopeConstants) {
|
||||
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
|
||||
if (asmName != null && !added.contains(asmName)) {
|
||||
asm.addConstant(asmName.replace("#", "_").replace("$", "_"), AsmFragment.getAsmConstant(program, constantVar.getValue(), 99));
|
||||
added.add(asmName);
|
||||
if(! (constantVar.getValue() instanceof ConstantArray)) {
|
||||
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
|
||||
if (asmName != null && !added.contains(asmName)) {
|
||||
asm.addConstant(asmName.replace("#", "_").replace("$", "_"), AsmFragment.getAsmConstant(program, constantVar.getValue(), 99));
|
||||
added.add(asmName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data directives for constants declarations
|
||||
*
|
||||
* @param asm The ASM program
|
||||
* @param scopeRef The scope
|
||||
*/
|
||||
private void addData(AsmProgram asm, ScopeRef scopeRef) {
|
||||
Scope scope = program.getScope().getScope(scopeRef);
|
||||
Collection<ConstantVar> scopeConstants = scope.getAllConstants(false);
|
||||
Set<String> added = new LinkedHashSet<>();
|
||||
for (ConstantVar constantVar : scopeConstants) {
|
||||
if(constantVar.getValue() instanceof ConstantArray) {
|
||||
ConstantArray constantArray = (ConstantArray) constantVar.getValue();
|
||||
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
|
||||
if (asmName != null && !added.contains(asmName)) {
|
||||
|
||||
List<String> asmElements = new ArrayList<>();
|
||||
for (ConstantValue element : constantArray.getElements()) {
|
||||
String asmElement = AsmFragment.getAsmConstant(program, element, 99);
|
||||
asmElements.add(asmElement);
|
||||
}
|
||||
if(SymbolTypeBasic.BYTE.equals(constantArray.getElementType())) {
|
||||
asm.addData(asmName.replace("#", "_").replace("$", "_"), AsmData.Type.BYTE, asmElements);
|
||||
added.add(asmName);
|
||||
} else {
|
||||
throw new RuntimeException("Unhandled constant array element type "+constantArray.toString(program));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add label declarations for all scope variables assigned to ZP registers
|
||||
|
19
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.asm
Normal file
19
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.asm
Normal file
@ -0,0 +1,19 @@
|
||||
.const SCREEN = $400
|
||||
TXT: .byte 3, 1, $d, 5, $c, $f, $14, $20
|
||||
jsr main
|
||||
main: {
|
||||
ldx #0
|
||||
ldy #0
|
||||
b1:
|
||||
lda TXT,y
|
||||
sta SCREEN,x
|
||||
iny
|
||||
cpy #8
|
||||
bne b2
|
||||
ldy #0
|
||||
b2:
|
||||
inx
|
||||
cpx #$65
|
||||
bne b1
|
||||
rts
|
||||
}
|
23
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.cfg
Normal file
23
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.cfg
Normal file
@ -0,0 +1,23 @@
|
||||
@begin: scope:[] from
|
||||
[0] call main param-assignment [ ]
|
||||
to:@end
|
||||
@end: scope:[] from @begin
|
||||
main: scope:[main] from @begin
|
||||
[1] phi() [ ]
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
[2] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) [ main::j#3 main::i#2 ]
|
||||
[2] (byte) main::j#3 ← phi( main/(byte) 0 main::@2/(byte) main::j#4 ) [ main::j#3 main::i#2 ]
|
||||
[3] (byte~) main::$0 ← (const byte[]) TXT#0 *idx (byte) main::j#3 [ main::j#3 main::i#2 main::$0 ]
|
||||
[4] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte~) main::$0 [ main::j#3 main::i#2 ]
|
||||
[5] (byte) main::j#1 ← ++ (byte) main::j#3 [ main::i#2 main::j#1 ]
|
||||
[6] if((byte) main::j#1!=(byte) 8) goto main::@2 [ main::i#2 main::j#1 ]
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1 main::@1
|
||||
[7] (byte) main::j#4 ← phi( main::@1/(byte) main::j#1 main::@1/(byte) 0 ) [ main::i#2 main::j#4 ]
|
||||
[8] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::j#4 main::i#1 ]
|
||||
[9] if((byte) main::i#1!=(byte) 101) goto main::@1 [ main::j#4 main::i#1 ]
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@2
|
||||
[10] return [ ]
|
||||
to:@return
|
1203
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.log
Normal file
1203
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.log
Normal file
File diff suppressed because it is too large
Load Diff
22
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.sym
Normal file
22
src/main/java/dk/camelot64/kickc/test/ref/inmemarray.sym
Normal file
@ -0,0 +1,22 @@
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte*) SCREEN
|
||||
(const byte*) SCREEN#0 SCREEN = (word) 1024
|
||||
(byte[]) TXT
|
||||
(const byte[]) TXT#0 TXT = { (byte) 3, (byte) 1, (byte) 13, (byte) 5, (byte) 12, (byte) 15, (byte) 20, (byte) 32 }
|
||||
(void()) main()
|
||||
(byte~) main::$0 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@return
|
||||
(byte) main::i
|
||||
(byte) main::i#1 reg byte x 16.5
|
||||
(byte) main::i#2 reg byte x 5.5
|
||||
(byte) main::j
|
||||
(byte) main::j#1 reg byte y 16.5
|
||||
(byte) main::j#3 reg byte y 11.0
|
||||
(byte) main::j#4 reg byte y 7.333333333333333
|
||||
|
||||
reg byte y [ main::j#3 main::j#4 main::j#1 ]
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
reg byte a [ main::$0 ]
|
Loading…
Reference in New Issue
Block a user