1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Added support for ASM .byte directives through array initializers.

This commit is contained in:
jespergravgaard 2017-10-17 13:34:41 +02:00
parent feba0565b4
commit 1a649008b9
13 changed files with 1526 additions and 25 deletions

View File

@ -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.

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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");

View 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();
}
}

View 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();
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View 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 ]