1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-26 03:32:23 +00:00

Implemented syntax for composing word from two bytes word w = { b1, b2 };

Optimizer still needs to attempt to (and allow) placing the two byte variables directly at the lo/hi-vars of the word wo minimize moving data around.

Closes #37
This commit is contained in:
jespergravgaard 2017-12-25 23:27:54 +01:00
parent bd3c26b5ec
commit e3353acbc9
29 changed files with 2171 additions and 324 deletions

View File

@ -105,24 +105,26 @@ public class Compiler {
}
private Program pass1GenerateSSA() {
new Pass1FixLvalueLoHi(program).execute();
new Pass1TypeInference(program).execute();
getLog().append("PROGRAM");
getLog().append(program.getStatementSequence().toString(program));
getLog().append("SYMBOLS");
getLog().append(program.getScope().getSymbolTableContents(program));
new Pass1GenerateControlFlowGraph(program).execute();
new Pass1GenerateControlFlowGraph(program).execute();
new Pass1AddTypePromotions(program).execute();
getLog().append("INITIAL CONTROL FLOW GRAPH");
getLog().append(program.getGraph().toString(program));
new Pass1AssertReturn(program).execute();
new Pass1AssertUsedVars(program).execute();
new Pass1ExtractInlineStrings(program).execute();
new Pass1EliminateUncalledProcedures(program).execute();
new Pass1EliminateUnusedVars(program).execute();
new Pass1ExtractInlineStrings(program).execute();
new Pass1FixWordConstructors(program).execute();
new Pass1EliminateEmptyBlocks(program).execute();
getLog().append("CONTROL FLOW GRAPH");

View File

@ -146,6 +146,7 @@ public class AsmFragmentManager {
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbuaa", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbsz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbsaa", null, mapZ));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz2", ".*=.*aa.*|.*z2.*z2.*", "lda {z2}\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("(.*)=(.*)_vbuz3", ".*=.*aa.*|.*z3.*z3.*", "lda {z3}\n", "$1=$2_vbuaa", null, null));
synths.add(new FragmentSynthesis("vbuz1=vbuz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbuaa=vbuaa$1", "sta {z1}\n", mapZ));
synths.add(new FragmentSynthesis("vbsz1=vbsz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbsaa=vbsaa$1", "sta {z1}\n", mapZ));

View File

@ -0,0 +1,2 @@
sta {z1}
sta {z1}+1

View File

@ -0,0 +1,2 @@
sta {z1}+1
stx {z1}

View File

@ -0,0 +1,2 @@
sta {z1}+1
sty {z1}

View File

@ -0,0 +1,3 @@
sta {z1}+1
lda {z2}
sta {z1}

View File

@ -0,0 +1,2 @@
stx {z1}+1
sta {z1}

View File

@ -0,0 +1,2 @@
stx {z1}+1
sty {z1}

View File

@ -0,0 +1,2 @@
sty {z1}+1
sta {z1}

View File

@ -0,0 +1,2 @@
sty {z1}+1
stx {z1}

View File

@ -0,0 +1,3 @@
sta {z1}
lda {z2}
sta {z1}+1

View File

@ -120,8 +120,7 @@ public class Operator {
public static final Operator BOOL_NOT = new Operator("~", "_not_", Type.UNARY, 2);
public static final Operator NOT = new Operator("!", "_not_", Type.UNARY, 2);
public static final Operator DEREF = new Operator("*", "_deref_", Type.UNARY, 2);
public static final Operator LOWBYTE = new Operator("<", "_lo_", Type.UNARY, 2);
public static final Operator HIBYTE = new Operator(">", "_hi_", Type.UNARY, 2);
public static final Operator WORD = new Operator("w=", "_word_", Type.BINARY, 2);
public static final Operator DEREF_IDX = new Operator("*idx", "_derefidx_", Type.BINARY, 2);
public static final Operator SET_LOWBYTE = new Operator("lo=", "_setlo_", Type.BINARY, 2);
public static final Operator SET_HIBYTE = new Operator("hi=", "_sethi_", Type.BINARY, 2);
@ -136,17 +135,19 @@ public class Operator {
public static final Operator MINUS = new Operator("-", "_minus_", Type.BINARY, 4);
public static final Operator SHIFT_LEFT = new Operator("<<", "_rol_", Type.BINARY, 5);
public static final Operator SHIFT_RIGHT = new Operator(">>", "_ror_", Type.BINARY, 5);
public static final Operator LT = new Operator("<", "_lt_", Type.BINARY, 6);
public static final Operator LE = new Operator("<=", "_le_", Type.BINARY, 6);
public static final Operator GT = new Operator(">", "_gt_", Type.BINARY, 6);
public static final Operator GE = new Operator(">=", "_ge_", Type.BINARY, 6);
public static final Operator EQ = new Operator("==", "_eq_", Type.BINARY, 7);
public static final Operator NEQ = new Operator("!=", "_neq_", Type.BINARY, 7);
public static final Operator BOOL_AND = new Operator("&", "_band_", Type.BINARY, 8);
public static final Operator BOOL_XOR = new Operator("^", "_bxor_", Type.BINARY, 9);
public static final Operator BOOL_OR = new Operator("|", "_bor_", Type.BINARY, 10);
public static final Operator LOGIC_AND = new Operator("&&", "_and_", Type.BINARY, 11);
public static final Operator LOGIC_OR = new Operator("||", "_or_", Type.BINARY, 12);
public static final Operator LOWBYTE = new Operator("<", "_lo_", Type.UNARY, 6);
public static final Operator HIBYTE = new Operator(">", "_hi_", Type.UNARY, 6);
public static final Operator LT = new Operator("<", "_lt_", Type.BINARY, 7);
public static final Operator LE = new Operator("<=", "_le_", Type.BINARY, 7);
public static final Operator GT = new Operator(">", "_gt_", Type.BINARY, 7);
public static final Operator GE = new Operator(">=", "_ge_", Type.BINARY, 7);
public static final Operator EQ = new Operator("==", "_eq_", Type.BINARY, 8);
public static final Operator NEQ = new Operator("!=", "_neq_", Type.BINARY, 8);
public static final Operator BOOL_AND = new Operator("&", "_band_", Type.BINARY, 9);
public static final Operator BOOL_XOR = new Operator("^", "_bxor_", Type.BINARY, 10);
public static final Operator BOOL_OR = new Operator("|", "_bor_", Type.BINARY, 11);
public static final Operator LOGIC_AND = new Operator("&&", "_and_", Type.BINARY, 12);
public static final Operator LOGIC_OR = new Operator("||", "_or_", Type.BINARY, 13);
public String getOperator() {
return operator;

View File

@ -67,11 +67,11 @@ public class SymbolTypeInference {
throw new RuntimeException("Type error: Dereferencing a non-pointer " + subType);
}
} else if (Operator.LOWBYTE.equals(operator)) {
if (subType instanceof SymbolTypePointer || SymbolType.WORD.equals(subType) || SymbolType.WORD.equals(subType)) {
if (subType instanceof SymbolTypePointer || SymbolType.isWord(subType) || SymbolType.isSWord(subType)) {
return SymbolType.BYTE;
}
} else if (Operator.HIBYTE.equals(operator)) {
if (subType instanceof SymbolTypePointer || SymbolType.WORD.equals(subType) || SymbolType.SWORD.equals(subType)) {
if (subType instanceof SymbolTypePointer || SymbolType.isWord(subType) || SymbolType.isSWord(subType) ) {
return SymbolType.BYTE;
}
} else if (Operator.CAST_BYTE.equals(operator)) {
@ -98,6 +98,8 @@ public class SymbolTypeInference {
return type1;
} else if (Operator.SET_LOWBYTE.equals(operator)) {
return type1;
} else if (Operator.WORD.equals(operator)) {
return SymbolType.WORD;
}
String op = operator.getOperator();

View File

@ -83,11 +83,12 @@ expr
| '(' typeDecl ')' expr #exprCast
| expr '[' expr ']' #exprArray
| ('--' | '++' ) expr #exprPreMod
| expr ('--' | '++' )#exprPostMod
| ('+' | '-' | '!' | '&' | '*' | '~' | '<' | '>') expr #exprUnary
| expr ('--' | '++' ) #exprPostMod
| ('+' | '-' | '!' | '&' | '*' | '~') expr #exprUnary
| expr ('>>' | '<<' ) expr #exprBinary
| expr ('*' | '/' | '%' ) expr #exprBinary
| expr ( '+' | '-') expr #exprBinary
| ('<' | '>') expr #exprUnary
| expr ( '==' | '!=' | '<>' | '<' | '<=' | '=<' | '>=' | '=>' | '>' ) expr #exprBinary
| expr ( '&' ) expr #exprBinary
| expr ( '^' ) expr #exprBinary

View File

@ -1,4 +1,4 @@
// Generated from C:/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
// Generated from /Users/jespergravgaard/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.ParserRuleContext;

View File

@ -1,4 +1,4 @@
// Generated from C:/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
// Generated from /Users/jespergravgaard/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;

View File

@ -1,4 +1,4 @@
// Generated from C:/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
// Generated from /Users/jespergravgaard/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;

View File

@ -1,4 +1,4 @@
// Generated from C:/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
// Generated from /Users/jespergravgaard/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.ParseTreeListener;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Generated from C:/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser\KickC.g4 by ANTLR 4.7
// Generated from /Users/jespergravgaard/c64/src/kickc/src/main/java/dk/camelot64/kickc/parser/KickC.g4 by ANTLR 4.7
package dk.camelot64.kickc.parser;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;

View File

@ -5,7 +5,7 @@ import dk.camelot64.kickc.model.*;
import java.util.*;
/**
* Eliminate uncalled methods
* All inline strings in the code are extracted into constants.
*/
public class Pass1ExtractInlineStrings extends Pass1Base {
@ -22,43 +22,44 @@ public class Pass1ExtractInlineStrings extends Pass1Base {
Statement statement = stmtIt.next();
if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
ListIterator<RValue> parIt = call.getParameters().listIterator();
int parNum = 0;
while (parIt.hasNext()) {
RValue parameter = parIt.next();
if (parameter instanceof ConstantString) {
Procedure procedure = getProgram().getScope().getProcedure(call.getProcedure());
String parameterName = procedure.getParameterNames().get(parNum);
ConstantVar strConst = createStringConstantVar(blockScope, (ConstantString) parameter, parameterName);
parIt.set(strConst.getRef());
}
parNum++;
List<RValue> parameters = call.getParameters();
int size = parameters.size();
for (int i = 0; i < size; i++) {
String parameterName = procedure.getParameterNames().get(i);
execute(new VariableReplacer.ReplacableCallParameter(call, i), blockScope, parameterName);
}
} else if (statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
if(assignment.getrValue1() instanceof ConstantString && assignment.getrValue2() instanceof ConstantString) {
if(assignment.getrValue1()==null && assignment.getOperator()==null && assignment.getrValue2() instanceof ConstantString) {
// This will be picked up later as a constant - the temporary constant variable is not needed
continue;
}
if (assignment.getrValue1() instanceof ConstantString) {
ConstantVar strConst = createStringConstantVar(blockScope, (ConstantString) assignment.getrValue1(), null);
assignment.setrValue1(strConst.getRef());
}
if (assignment.getrValue2() instanceof ConstantString && assignment.getOperator() != null) {
ConstantVar strConst = createStringConstantVar(blockScope, (ConstantString) assignment.getrValue2(), null);
assignment.setrValue2(strConst.getRef());
if(assignment.getrValue1() instanceof ConstantString && assignment.getrValue2() instanceof ConstantString) {
// This will be picked up later as a constant - the temporary constant variable is not needed
continue;
}
execute(new VariableReplacer.ReplacableRValue1(assignment), blockScope, null);
execute(new VariableReplacer.ReplacableRValue2(assignment), blockScope, null);
} else if (statement instanceof StatementReturn) {
StatementReturn statementReturn = (StatementReturn) statement;
if (statementReturn.getValue() instanceof ConstantString) {
ConstantVar strConst = createStringConstantVar(blockScope, (ConstantString) statementReturn.getValue(), null);
statementReturn.setValue(strConst.getRef());
}
execute(new VariableReplacer.ReplacableReturn((StatementReturn) statement), blockScope, null);
}
}
}
return false;
}
private void execute(VariableReplacer.ReplacableValue replacable, Scope blockScope, String nameHint) {
RValue value = replacable.get();
if(value instanceof ConstantString) {
ConstantVar strConst = createStringConstantVar(blockScope, (ConstantString) replacable.get(), nameHint);
replacable.set(strConst.getRef());
}
for (VariableReplacer.ReplacableValue subValue : replacable.getSubValues()) {
execute(subValue, blockScope, nameHint);
}
}
private ConstantVar createStringConstantVar(Scope blockScope, ConstantString constantString, String nameHint) {
String name;
if (nameHint == null) {

View File

@ -0,0 +1,46 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
/**
* The syntax for word constructors <code>word w = { b1, b2 };</code>
* is turned into a binary operator <code>word w = b1 _toword_ b2 ;</code>
*/
public class Pass1FixWordConstructors extends Pass1Base {
public Pass1FixWordConstructors(Program program) {
super(program);
}
@Override
boolean executeStep() {
ProgramScope programScope = getProgram().getScope();
for (ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for (Statement statement : block.getStatements()) {
if (statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
if(assignment.getrValue1()==null && assignment.getOperator()==null) {
if(assignment.getrValue2() instanceof ValueList) {
ValueList list = (ValueList) assignment.getrValue2();
if(list.getList().size()==2) {
// We have a simple assignment of a length 2 value list to a variable
SymbolType lType = SymbolTypeInference.inferType(programScope, assignment.getlValue());
SymbolType elmType1 = SymbolTypeInference.inferType(programScope, list.getList().get(0));
SymbolType elmType2 = SymbolTypeInference.inferType(programScope, list.getList().get(1));
if(SymbolType.isWord(lType) && SymbolType.isByte(elmType1) && SymbolType.isByte(elmType2)) {
// Types are word = { byte, byte } - perform the modification
assignment.setrValue1(list.getList().get(0));
assignment.setOperator(Operator.WORD);
assignment.setrValue2(list.getList().get(1));
getLog().append("Fixing word constructor with " + assignment.toString());
}
}
}
}
}
}
}
return false;
}
}

View File

@ -106,13 +106,20 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization {
*/
private Map<ConstantRef, ConstantValue> findAliasConstants() {
Map<ConstantRef, ConstantValue> aliases = new HashMap<>();
Collection<ConstantVar> allConstants = getProgram().getScope().getAllConstants(true);
ProgramScope programScope = getProgram().getScope();
Collection<ConstantVar> allConstants = programScope.getAllConstants(true);
for (ConstantVar constant : allConstants) {
ConstantValue constantValue = constant.getValue();
if(constantValue instanceof ConstantRef) {
if(((ConstantRef) constantValue).isIntermediate()) {
// The value is an intermediate constant - replace all uses of the intermediate with uses of the referer instead.
aliases.put((ConstantRef) constant.getValue(), constant.getRef());
constant.setValue(programScope.getConstant((ConstantRef) constantValue).getValue());
} else {
aliases.put(constant.getRef(), constant.getValue());
}
}
}
return aliases;
}

View File

@ -1,7 +1,7 @@
const byte* SCREEN = $400;
void main() {
byte[] his = { >SCREEN, >SCREEN+1 };
byte[] his = { >SCREEN, >SCREEN+$100 };
for( byte h: 0..1) {
for (byte l: 4..7) {
word w = { his[h], l };

View File

@ -105,10 +105,10 @@ print::@return: scope:[print] from print::@3
to:@end
@end: scope:[] from @2
Creating constant string variable for inline (const byte[]) main::msg "message 3 @"
Eliminating unused variable - keeping the call (void~) main::$0
Eliminating unused variable - keeping the call (void~) main::$1
Eliminating unused variable - keeping the call (void~) main::$2
Creating constant string variable for inline (const byte[]) main::msg "message 3 @"
Removing empty block print::@4
Removing empty block print::@3
Removing empty block print::@5

View File

@ -0,0 +1,34 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.const SCREEN = $400
jsr main
main: {
.label w = 3
.label sc = 5
.label l = 2
ldx #0
b1:
lda #4
sta l
b2:
lda his,x
sta w+1
lda l
sta w
sta sc
lda w+1
sta sc+1
ldy #0
lda #'*'
sta (sc),y
inc l
lda l
cmp #8
bne b2
inx
cpx #2
bne b1
rts
his: .byte >SCREEN, >SCREEN+$100
}

View File

@ -0,0 +1,31 @@
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @1
[3] phi() [ ] ( )
main: scope:[main] from @1
[4] phi() [ ] ( main:2 [ ] )
to:main::@1
main::@1: scope:[main] from main main::@3
[5] (byte) main::h#4 ← phi( main/(byte/signed byte/word/signed word) 0 main::@3/(byte) main::h#1 ) [ main::h#4 ] ( main:2 [ main::h#4 ] )
to:main::@2
main::@2: scope:[main] from main::@1 main::@2
[6] (byte) main::l#2 ← phi( main::@1/(byte/signed byte/word/signed word) 4 main::@2/(byte) main::l#1 ) [ main::h#4 main::l#2 ] ( main:2 [ main::h#4 main::l#2 ] )
[7] (byte~) main::$3 ← (const byte[]) main::his#0 *idx (byte) main::h#4 [ main::h#4 main::l#2 main::$3 ] ( main:2 [ main::h#4 main::l#2 main::$3 ] )
[8] (word) main::w#0 ← (byte~) main::$3 w= (byte) main::l#2 [ main::h#4 main::l#2 main::w#0 ] ( main:2 [ main::h#4 main::l#2 main::w#0 ] )
[9] (byte*) main::sc#0 ← ((byte*)) (word) main::w#0 [ main::h#4 main::l#2 main::sc#0 ] ( main:2 [ main::h#4 main::l#2 main::sc#0 ] )
[10] *((byte*) main::sc#0) ← (byte) '*' [ main::h#4 main::l#2 ] ( main:2 [ main::h#4 main::l#2 ] )
[11] (byte) main::l#1 ← ++ (byte) main::l#2 [ main::h#4 main::l#1 ] ( main:2 [ main::h#4 main::l#1 ] )
[12] if((byte) main::l#1!=(byte/signed byte/word/signed word) 8) goto main::@2 [ main::h#4 main::l#1 ] ( main:2 [ main::h#4 main::l#1 ] )
to:main::@3
main::@3: scope:[main] from main::@2
[13] (byte) main::h#1 ← ++ (byte) main::h#4 [ main::h#1 ] ( main:2 [ main::h#1 ] )
[14] if((byte) main::h#1!=(byte/signed byte/word/signed word) 2) goto main::@1 [ main::h#1 ] ( main:2 [ main::h#1 ] )
to:main::@return
main::@return: scope:[main] from main::@3
[15] return [ ] ( main:2 [ ] )
to:@return

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word) 1024
(void()) main()
(byte~) main::$3 reg byte a 202.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@return
(byte) main::h
(byte) main::h#1 reg byte x 16.5
(byte) main::h#4 reg byte x 15.375
(byte[]) main::his
(const byte[]) main::his#0 his = { >(const byte*) SCREEN#0, >(const byte*) SCREEN#0+(word/signed word) 256 }
(byte) main::l
(byte) main::l#1 l zp ZP_BYTE:2 151.5
(byte) main::l#2 l zp ZP_BYTE:2 60.599999999999994
(byte*) main::sc
(byte*) main::sc#0 sc zp ZP_WORD:5 202.0
(word) main::w
(word) main::w#0 w zp ZP_WORD:3 202.0
reg byte x [ main::h#4 main::h#1 ]
zp ZP_BYTE:2 [ main::l#2 main::l#1 ]
reg byte a [ main::$3 ]
zp ZP_WORD:3 [ main::w#0 ]
zp ZP_WORD:5 [ main::sc#0 ]