mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-18 08:30:18 +00:00
Fixed problem with nested do loops being mixed up during empty block culling. Improved flipper implementation.
This commit is contained in:
parent
d41f762a8b
commit
2e90516f34
@ -3,9 +3,11 @@ Features
|
||||
- Implement Register Allocation (that utilize real registers - and non-zeropage memory)
|
||||
- Add Fixed Point number types
|
||||
- Create a proper main function for the compiler
|
||||
- Add for loop
|
||||
- Add a for loop for(init;condition;increment) {stmt} -> { init; do { stmt; increment } while (condition) }
|
||||
- Add imports
|
||||
- Add structs
|
||||
- Add ++/-- incrementing/decrementing operators.
|
||||
- Let { stmt } introduce a new anonymous scope.
|
||||
- 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)
|
||||
- Add ability to call ASM code from KC.
|
||||
@ -13,7 +15,7 @@ Features
|
||||
- Add inline ASM (maybe?)
|
||||
|
||||
Process/Code Structure Improvement
|
||||
- Make each phase return a separate object graph (allowing for keeping the history in memory & performing rollbacks)
|
||||
- Make each phase return a separate object graph (allowing for keeeping the history in memory & performing rollbacks)
|
||||
- Implemenent Assertions for the output of different phases (ensuring that the result of the phase is consistent)
|
||||
- Refactor Expression Operator Implementation & Evaluation into one class per operator
|
||||
|
||||
@ -25,6 +27,7 @@ Testing
|
||||
- Add assert statements to the language. Create KC programs that test the compiler by compiling, running and testing assertions.
|
||||
|
||||
Optimizations
|
||||
- Optimize phi transitions by ensuring that identical phi-transitions with regards to register allocation are collected into a single transition.
|
||||
- Optimize register allocation by combining with knowledge of ASM program cost (bytes/cycles) and different ASM fragments with different clobbering.
|
||||
- Optimize by finding optimal sequence for multiple phi assignments in entry-segments.
|
||||
- Optimize by allowing resequencing of statements and phi assignemtns in a final phase. Perhaps by converting phi statements to "normal" statements and using some optimization step.
|
||||
|
1
src/dk/camelot64/kickc/asm/fragment/aby=_star_cowo1.asm
Normal file
1
src/dk/camelot64/kickc/asm/fragment/aby=_star_cowo1.asm
Normal file
@ -0,0 +1 @@
|
||||
lda {cowo1}
|
@ -0,0 +1,2 @@
|
||||
cmp #{coby1}
|
||||
bne {la1}
|
@ -0,0 +1 @@
|
||||
dec {zpby1}
|
@ -61,7 +61,9 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
|
||||
@Override
|
||||
public Void visitStmtBlock(KickCParser.StmtBlockContext ctx) {
|
||||
this.visit(ctx.stmtSeq());
|
||||
if(ctx.stmtSeq()!=null) {
|
||||
this.visit(ctx.stmtSeq());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -127,7 +129,9 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
Label beginJumpLabel = getCurrentSymbols().addLabelIntermediate();
|
||||
StatementLabel beginJumpTarget = new StatementLabel(beginJumpLabel);
|
||||
sequence.addStatement(beginJumpTarget);
|
||||
this.visit(ctx.stmt());
|
||||
if(ctx.stmt()!=null) {
|
||||
this.visit(ctx.stmt());
|
||||
}
|
||||
RValue rValue = (RValue) this.visit(ctx.expr());
|
||||
Statement doJmpStmt = new StatementConditionalJump(rValue, beginJumpLabel);
|
||||
sequence.addStatement(doJmpStmt);
|
||||
|
@ -11,17 +11,20 @@ public class Pass2CullEmptyBlocks extends Pass2SsaOptimization {
|
||||
|
||||
@Override
|
||||
public boolean optimize() {
|
||||
List<ControlFlowBlock> remove = new ArrayList<>();
|
||||
Map<Label, Label> replace = new HashMap<>();
|
||||
final List<ControlFlowBlock> remove = new ArrayList<>();
|
||||
for (ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
if (block.getStatements().isEmpty() && block.getLabel().isIntermediate()) {
|
||||
remove.add(block);
|
||||
}
|
||||
}
|
||||
for (ControlFlowBlock removeBlock : remove) {
|
||||
|
||||
for (final ControlFlowBlock removeBlock : remove) {
|
||||
ControlFlowBlock successor = getGraph().getDefaultSuccessor(removeBlock);
|
||||
for (ControlFlowBlock predecessor : getGraph().getPredecessors(removeBlock)) {
|
||||
replace.put(removeBlock.getLabel(), predecessor.getLabel());
|
||||
// Replace all jumps (default/conditional/call) to @removeBlock with a jump to the default successor
|
||||
final List<ControlFlowBlock> predecessors = getGraph().getPredecessors(removeBlock);
|
||||
for (ControlFlowBlock predecessor : predecessors) {
|
||||
Map<Label, Label> replace = new HashMap<>();
|
||||
replace.put(removeBlock.getLabel(), successor.getLabel());
|
||||
if (removeBlock.getLabel().equals(predecessor.getDefaultSuccessor())) {
|
||||
predecessor.setDefaultSuccessor(successor.getLabel());
|
||||
}
|
||||
@ -31,12 +34,35 @@ public class Pass2CullEmptyBlocks extends Pass2SsaOptimization {
|
||||
if (removeBlock.getLabel().equals(predecessor.getCallSuccessor())) {
|
||||
predecessor.setCallSuccessor(successor.getLabel());
|
||||
}
|
||||
replaceLabels(predecessor, replace);
|
||||
}
|
||||
// In all phi functions of a successor blocks make a copy of the phi assignment for each predecessor
|
||||
ControlFlowGraphBaseVisitor<Void> phiFixVisitor = new ControlFlowGraphBaseVisitor<Void>() {
|
||||
@Override
|
||||
public Void visitPhi(StatementPhi phi) {
|
||||
for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) {
|
||||
if(previousSymbol.getBlock().equals(removeBlock.getLabel())) {
|
||||
// Found a phi function referencing the remove block - add copies for each predecessor
|
||||
RValue previousRValue = previousSymbol.getRValue();
|
||||
for (ControlFlowBlock predecessor : predecessors) {
|
||||
if(previousSymbol!=null) {
|
||||
previousSymbol.setBlock(predecessor.getLabel());
|
||||
previousSymbol = null;
|
||||
} else {
|
||||
phi.addPreviousVersion(predecessor.getLabel(), previousRValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
phiFixVisitor.visitBlock(successor);
|
||||
getGraph().getAllBlocks().remove(removeBlock);
|
||||
getSymbols().remove(removeBlock.getLabel());
|
||||
System.out.println("Culled Empty Block " + removeBlock.getLabel());
|
||||
}
|
||||
replaceLabels(replace);
|
||||
return remove.size()>0;
|
||||
}
|
||||
|
||||
|
@ -171,38 +171,53 @@ public abstract class Pass2SsaOptimization {
|
||||
* @param replacements Variables that have alias values.
|
||||
*/
|
||||
public void replaceLabels(final Map<Label, Label> replacements) {
|
||||
ControlFlowGraphBaseVisitor<Void> visitor = new ControlFlowGraphBaseVisitor<Void>() {
|
||||
|
||||
@Override
|
||||
public Void visitConditionalJump(StatementConditionalJump conditionalJump) {
|
||||
if (getReplacement(replacements, conditionalJump.getDestination()) != null) {
|
||||
conditionalJump.setDestination(getReplacement(replacements, conditionalJump.getDestination()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitJump(StatementJump jump) {
|
||||
if (getReplacement(replacements, jump.getDestination()) != null) {
|
||||
jump.setDestination(getReplacement(replacements, jump.getDestination()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPhi(StatementPhi phi) {
|
||||
for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) {
|
||||
Label replacement = getReplacement(replacements, previousSymbol.getBlock());
|
||||
if (replacement != null) {
|
||||
previousSymbol.setBlock(replacement);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
ControlFlowGraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements);
|
||||
visitor.visitGraph(graph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all usages of a label in statements with another label.
|
||||
*
|
||||
* @param replacements Variables that have alias values.
|
||||
*/
|
||||
public void replaceLabels(ControlFlowBlock block, final Map<Label, Label> replacements) {
|
||||
ControlFlowGraphBaseVisitor<Void> visitor = getLabelReplaceVisitor(replacements);
|
||||
visitor.visitBlock(block);
|
||||
}
|
||||
|
||||
/** Creates a visitor that can replace labels. */
|
||||
private ControlFlowGraphBaseVisitor<Void> getLabelReplaceVisitor(final Map<Label, Label> replacements) {
|
||||
return new ControlFlowGraphBaseVisitor<Void>() {
|
||||
|
||||
@Override
|
||||
public Void visitConditionalJump(StatementConditionalJump conditionalJump) {
|
||||
if (getReplacement(replacements, conditionalJump.getDestination()) != null) {
|
||||
conditionalJump.setDestination(getReplacement(replacements, conditionalJump.getDestination()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitJump(StatementJump jump) {
|
||||
if (getReplacement(replacements, jump.getDestination()) != null) {
|
||||
jump.setDestination(getReplacement(replacements, jump.getDestination()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPhi(StatementPhi phi) {
|
||||
for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) {
|
||||
Label replacement = getReplacement(replacements, previousSymbol.getBlock());
|
||||
if (replacement != null) {
|
||||
previousSymbol.setBlock(replacement);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label to use as replacement for another label.
|
||||
*
|
||||
@ -244,13 +259,13 @@ public abstract class Pass2SsaOptimization {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove variables from the symbol table
|
||||
* Remove symbols from the symbol table
|
||||
*
|
||||
* @param variables The variables to remove
|
||||
* @param symbols The symbols to remove
|
||||
*/
|
||||
public void deleteSymbols(Collection<? extends LValue> variables) {
|
||||
for (LValue variable : variables) {
|
||||
scope.remove((Symbol) variable);
|
||||
public void deleteSymbols(Collection<? extends Symbol> symbols) {
|
||||
for (Symbol symbol : symbols) {
|
||||
symbol.getScope().remove(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,13 @@ public class Pass3RegisterAllocation {
|
||||
allocation.allocate(symbols.getVariable("flip::i#2"), RegisterAllocation.getRegisterX());
|
||||
allocation.allocate(symbols.getVariable("flip::$0"), RegisterAllocation.getRegisterA());
|
||||
allocation.allocate(symbols.getVariable("flip::$8"), RegisterAllocation.getRegisterA());
|
||||
allocation.allocate(symbols.getVariable("$1"), RegisterAllocation.getRegisterA());
|
||||
allocation.allocate(symbols.getVariable("$3"), RegisterAllocation.getRegisterA());
|
||||
allocation.allocate(symbols.getVariable("c#0"), RegisterAllocation.getRegisterX());
|
||||
allocation.allocate(symbols.getVariable("c#1"), RegisterAllocation.getRegisterX());
|
||||
allocation.allocate(symbols.getVariable("c#2"), RegisterAllocation.getRegisterX());
|
||||
allocation.allocate(symbols.getVariable("c#3"), RegisterAllocation.getRegisterX());
|
||||
allocation.allocate(symbols.getVariable("c#4"), RegisterAllocation.getRegisterX());
|
||||
|
||||
symbols.setAllocation(allocation);
|
||||
|
||||
@ -137,6 +144,8 @@ public class Pass3RegisterAllocation {
|
||||
currentZp = currentZp + 2;
|
||||
} else if (symbol.getType().equals(SymbolTypeBasic.BOOLEAN)) {
|
||||
allocation.allocate(var, new RegisterAllocation.RegisterZpBool(currentZp++));
|
||||
} else if (symbol.getType().equals(SymbolTypeBasic.VOID)) {
|
||||
// No need to allocate register for VOID value
|
||||
} else if (symbol.getType() instanceof SymbolTypePointer) {
|
||||
allocation.allocate(var, new RegisterAllocation.RegisterZpPointerByte(currentZp));
|
||||
currentZp = currentZp + 2;
|
||||
|
@ -2,10 +2,18 @@ byte[1000] SCREEN = $0400;
|
||||
byte[16*16] buffer = $1000;
|
||||
byte[16*16] buffer2 = $1100;
|
||||
|
||||
byte *RASTER = $d012;
|
||||
|
||||
prepare();
|
||||
do {
|
||||
plot();
|
||||
byte c = 25;
|
||||
do{
|
||||
do { } while(*RASTER!=254)
|
||||
do { } while(*RASTER!=255)
|
||||
c=c-1;
|
||||
} while(c!=0)
|
||||
flip();
|
||||
plot();
|
||||
} while(true)
|
||||
|
||||
// Prepare buffer
|
||||
@ -21,18 +29,18 @@ void prepare() {
|
||||
void flip() {
|
||||
byte srcIdx = 0;
|
||||
byte dstIdx = 15;
|
||||
byte r=0;
|
||||
byte r=16;
|
||||
do {
|
||||
byte c = 0;
|
||||
byte c = 16;
|
||||
do {
|
||||
buffer2[dstIdx] = buffer[srcIdx];
|
||||
srcIdx = srcIdx+1;
|
||||
dstIdx = dstIdx+16;
|
||||
c=c+1;
|
||||
} while(c<16)
|
||||
r=r+1;
|
||||
c=c-1;
|
||||
} while(c!=0)
|
||||
dstIdx = dstIdx-1;
|
||||
} while(r<16)
|
||||
r=r-1;
|
||||
} while(r!=0)
|
||||
byte i=0;
|
||||
do {
|
||||
buffer[i] = buffer2[i];
|
||||
@ -43,7 +51,7 @@ void flip() {
|
||||
// Plot buffer on screen
|
||||
void plot() {
|
||||
byte* line = SCREEN+5*40+12;
|
||||
byte y=0;
|
||||
byte y=16;
|
||||
byte i=0;
|
||||
do {
|
||||
byte x=0;
|
||||
@ -53,6 +61,6 @@ void plot() {
|
||||
i=i+1;
|
||||
} while(x<16)
|
||||
line = line+40;
|
||||
y=y+1;
|
||||
} while(y<16)
|
||||
y=y-1;
|
||||
} while(y!=0)
|
||||
}
|
Loading…
Reference in New Issue
Block a user