Skip to content

Commit d1a55ed

Browse files
WIP TypeState compiler will have type information in the IR and use this to build typestate per basic block
Set max local reg and max stack size Rename FunctionBuilder to BytecodeFunction Revised call instruction Revised temp register assignment to start from maxLocalReg rather than 0 so that we have a single set of registers per function Initial work on Interpreter Generate more specific array store/load and field set/get instructions rather than move. More specific instructions for creating new instances of arrays and structs Revised handling of call instruction -- copy args to new frame and copy result back unary op in interpreter Revised handling of call instruction -- copy args to new frame and copy result back unary op in interpreter Fix issue with return a[1] Add support for arrays and structs in the interpreter. Renames in stackvm module Register type fix refactor fix refactor Update README Update README Update README
1 parent 6f41848 commit d1a55ed

File tree

18 files changed

+811
-128
lines changed

18 files changed

+811
-128
lines changed

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,45 @@
22

33
This project is part of the https://compilerprogramming.github.io/ project.
44

5-
The EZ (pronounced EeeZee) programming language is designed to allow us to learn various compiler techniques.
5+
The EZ (pronounced EeZee) programming language is designed to allow us to learn various compiler techniques.
6+
7+
## The EZ Language
8+
9+
The EZ programming language is a tiny statically typed language with syntax inspired by Swift.
10+
The language has the following features:
11+
12+
* Integer, Struct and 1-Dimensional Array types
13+
* If and While statements
14+
* Functions
15+
16+
The language syntax is described [ANTLR Grammar](antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4).
17+
The language is intentionally very simple and is meant to have just enough functionality to experiment with compiler implementation techniques.
18+
19+
## Modules
20+
21+
The project is under development and subject to change. At this point in time, we have following initial implementations:
22+
23+
* lexer - a simple tokenizer
24+
* parser - a recursive descent parser and AST
25+
* types - the type definitions
26+
* semantic - semantic analyzer
27+
* stackvm - a bytecode compiler that generates stack IR (bytecode interpreter not yet available)
28+
* registervm - a bytecode compiler that generates a linear register IR and a bytecode interpreter that can execute the IR
29+
30+
## How can you contribute?
31+
32+
Obviously firstly any contributes that improve and fix bugs are welcome. I am not keen on language extensions at this stage, but eventually
33+
we will be extending the language to explore more advanced features.
34+
35+
I am also interested in creating implementations of this project in C++, Go, Rust, swift, D, C, etc. If you are interested in working on such a
36+
port please contact me via [Discussions](https://github.com/orgs/CompilerProgramming/discussions).
37+
38+
## Community Discussions
39+
40+
There is a [community discussion forum](https://github.com/orgs/CompilerProgramming/discussions).
41+
42+
## What's next
43+
44+
The project has only just got started, there is lots to do!. See the plan in the [website](https://compilerprogramming.github.io/).
645
More documentation to follow, but for now please refer to the source code and the site above.
46+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.compilerprogramming.ezlang.exceptions;
2+
3+
public class InterpreterException extends RuntimeException {
4+
public InterpreterException(String message) {super(message);}
5+
public InterpreterException(String message, Throwable cause) {
6+
super(message, cause);
7+
}
8+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package com.compilerprogramming.ezlang.bytecode;
22

33
import com.compilerprogramming.ezlang.types.Symbol;
4+
import com.compilerprogramming.ezlang.types.Type;
45
import com.compilerprogramming.ezlang.types.TypeDictionary;
56

6-
public class RegisterVMCompiler {
7+
public class BytecodeCompiler {
78

89
public void compile(TypeDictionary typeDictionary) {
910
for (Symbol symbol: typeDictionary.getLocalSymbols()) {
1011
if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
11-
functionSymbol.code = new FunctionBuilder(functionSymbol);
12+
Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
13+
functionType.code = new BytecodeFunction(functionSymbol);
1214
}
1315
}
1416
}

registervm/src/main/java/com/compilerprogramming/ezlang/bytecode/FunctionBuilder.java renamed to registervm/src/main/java/com/compilerprogramming/ezlang/bytecode/BytecodeFunction.java

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import java.util.ArrayList;
1010
import java.util.List;
1111

12-
public class FunctionBuilder {
12+
public class BytecodeFunction {
1313

1414
public BasicBlock entry;
1515
public BasicBlock exit;
16+
public int maxLocalReg;
17+
public int maxStackSize;
1618
private int bid = 0;
1719
private BasicBlock currentBlock;
1820
private BasicBlock currentBreakTarget;
@@ -28,7 +30,7 @@ public class FunctionBuilder {
2830
*/
2931
private List<Operand> virtualStack = new ArrayList<>();
3032

31-
public FunctionBuilder(Symbol.FunctionTypeSymbol functionSymbol) {
33+
public BytecodeFunction(Symbol.FunctionTypeSymbol functionSymbol) {
3234
AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
3335
setVirtualRegisters(funcDecl.scope);
3436
this.bid = 0;
@@ -40,6 +42,10 @@ public FunctionBuilder(Symbol.FunctionTypeSymbol functionSymbol) {
4042
exitBlockIfNeeded();
4143
}
4244

45+
public int frameSize() {
46+
return maxLocalReg+maxStackSize;
47+
}
48+
4349
private void exitBlockIfNeeded() {
4450
if (currentBlock != null &&
4551
currentBlock != exit) {
@@ -57,6 +63,8 @@ private void setVirtualRegisters(Scope scope) {
5763
}
5864
}
5965
scope.maxReg = reg;
66+
if (maxLocalReg < scope.maxReg)
67+
maxLocalReg = scope.maxReg;
6068
for (Scope childScope: scope.children) {
6169
setVirtualRegisters(childScope);
6270
}
@@ -78,9 +86,11 @@ private void compileBlock(AST.BlockStmt block) {
7886

7987
private void compileReturn(AST.ReturnStmt returnStmt) {
8088
if (returnStmt.expr != null) {
81-
compileExpr(returnStmt.expr);
89+
boolean isIndexed = compileExpr(returnStmt.expr);
90+
if (isIndexed)
91+
codeIndexedLoad();
8292
if (virtualStack.size() == 1)
83-
code(new Instruction.Move(pop(), new Operand.ReturnRegisterOperand()));
93+
code(new Instruction.Return(pop()));
8494
else if (virtualStack.size() > 1)
8595
throw new CompilerException("Virtual stack has more than one item at return");
8696
}
@@ -269,32 +279,35 @@ private boolean compileExpr(AST.Expr expr) {
269279

270280
private boolean compileCallExpr(AST.CallExpr callExpr) {
271281
compileExpr(callExpr.callee);
272-
var callee = top();
273-
if (!(callee instanceof Operand.TempRegisterOperand) ) {
274-
var origCallee = pop();
275-
callee = createTemp();
276-
code(new Instruction.Move(origCallee, callee));
277-
}
278-
List<Operand> args = new ArrayList<>();
282+
var callee = pop();
283+
Type.TypeFunction calleeType = null;
284+
if (callee instanceof Operand.LocalFunctionOperand functionOperand)
285+
calleeType = functionOperand.functionType;
286+
else throw new CompilerException("Cannot call a non function type");
287+
var returnStackPos = virtualStack.size();
288+
List<Operand.RegisterOperand> args = new ArrayList<>();
279289
for (AST.Expr expr: callExpr.args) {
280290
boolean indexed = compileExpr(expr);
281291
if (indexed)
282292
codeIndexedLoad();
283293
var arg = top();
284294
if (!(arg instanceof Operand.TempRegisterOperand) ) {
285295
var origArg = pop();
286-
arg = createTemp();
296+
arg = createTemp(origArg.type);
287297
code(new Instruction.Move(origArg, arg));
288298
}
289-
args.add(arg);
299+
args.add((Operand.RegisterOperand) arg);
290300
}
291-
code(new Instruction.Call(callee, args.toArray(new Operand[args.size()])));
292-
// Similute the actions on the stack
293-
for (int i = 0; i < args.size()+1; i++)
301+
// Simulate the actions on the stack
302+
for (int i = 0; i < args.size(); i++)
294303
pop();
304+
Operand.TempRegisterOperand ret = null;
295305
if (callExpr.callee.type instanceof Type.TypeFunction tf &&
296-
tf.returnType != null)
297-
createTemp();
306+
!(tf.returnType instanceof Type.TypeVoid)) {
307+
ret = createTemp(tf.returnType);
308+
assert ret.regnum-maxLocalReg == returnStackPos;
309+
}
310+
code(new Instruction.Call(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()])));
298311
return false;
299312
}
300313

@@ -347,13 +360,20 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
347360
}
348361

349362
private void codeNew(Type type) {
350-
var temp = createTemp();
351-
code(new Instruction.Move(new Operand.NewTypeOperand(type), temp));
363+
var temp = createTemp(type);
364+
if (type instanceof Type.TypeArray typeArray) {
365+
code(new Instruction.NewArray(typeArray, temp));
366+
}
367+
else if (type instanceof Type.TypeStruct typeStruct) {
368+
code(new Instruction.NewStruct(typeStruct, temp));
369+
}
370+
else
371+
throw new CompilerException("Unexpected type: " + type);
352372
}
353373

354374
private void codeStoreAppend() {
355375
var operand = pop();
356-
code(new Instruction.AStoreAppend(top(), operand));
376+
code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
357377
}
358378

359379
private boolean compileNewExpr(AST.NewExpr newExpr) {
@@ -399,7 +419,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
399419
Operand right = pop();
400420
Operand left = pop();
401421
if (left instanceof Operand.ConstantOperand leftconstant &&
402-
right instanceof Operand.ConstantOperand rightconstant) {
422+
right instanceof Operand.ConstantOperand rightconstant) {
403423
long value = 0;
404424
switch (opCode) {
405425
case "+": value = leftconstant.value + rightconstant.value; break;
@@ -415,11 +435,11 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
415435
case ">=": value = leftconstant.value <= rightconstant.value ? 1 : 0; break;
416436
default: throw new CompilerException("Invalid binary op");
417437
}
418-
pushConstant(value);
438+
pushConstant(value, leftconstant.type);
419439
}
420440
else {
421-
var temp = createTemp();
422-
code(new Instruction.BinaryInstruction(opCode, temp, left, right));
441+
var temp = createTemp(binaryExpr.type);
442+
code(new Instruction.Binary(opCode, temp, left, right));
423443
}
424444
return false;
425445
}
@@ -433,35 +453,38 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
433453
Operand top = pop();
434454
if (top instanceof Operand.ConstantOperand constant) {
435455
switch (opCode) {
436-
case "-": pushConstant(-constant.value); break;
437-
case "!": pushConstant(constant.value == 0?1:0); break;
456+
case "-": pushConstant(-constant.value, constant.type); break;
457+
// Maybe below we should explicitly set Int
458+
case "!": pushConstant(constant.value == 0?1:0, constant.type); break;
438459
default: throw new CompilerException("Invalid unary op");
439460
}
440461
}
441462
else {
442-
var temp = createTemp();
443-
code(new Instruction.UnaryInstruction(opCode, temp, top));
463+
var temp = createTemp(unaryExpr.type);
464+
code(new Instruction.Unary(opCode, temp, top));
444465
}
445466
return false;
446467
}
447468

448469
private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
449-
pushConstant(constantExpr.value.num.intValue());
470+
pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
450471
return false;
451472
}
452473

453-
private void pushConstant(long value) {
454-
virtualStack.add(new Operand.ConstantOperand(value));
474+
private void pushConstant(long value, Type type) {
475+
pushOperand(new Operand.ConstantOperand(value, type));
455476
}
456477

457-
private Operand.TempRegisterOperand createTemp() {
458-
var tempRegister = new Operand.TempRegisterOperand(virtualStack.size());
459-
virtualStack.add(tempRegister);
478+
private Operand.TempRegisterOperand createTemp(Type type) {
479+
var tempRegister = new Operand.TempRegisterOperand(virtualStack.size()+maxLocalReg, type);
480+
pushOperand(tempRegister);
481+
if (maxStackSize < virtualStack.size())
482+
maxStackSize = virtualStack.size();
460483
return tempRegister;
461484
}
462485

463486
private void pushLocal(int regnum, String varName) {
464-
virtualStack.add(new Operand.LocalRegisterOperand(regnum, varName));
487+
pushOperand(new Operand.LocalRegisterOperand(regnum, varName));
465488
}
466489

467490
private void pushOperand(Operand operand) {
@@ -478,14 +501,28 @@ private Operand top() {
478501

479502
private void codeIndexedLoad() {
480503
Operand indexed = pop();
481-
var temp = createTemp();
482-
code(new Instruction.Move(indexed, temp));
504+
var temp = createTemp(indexed.type);
505+
if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
506+
code(new Instruction.ArrayLoad(loadIndexedOperand, temp));
507+
}
508+
else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
509+
code(new Instruction.GetField(loadFieldOperand, temp));
510+
}
511+
else
512+
code(new Instruction.Move(indexed, temp));
483513
}
484514

485515
private void codeIndexedStore() {
486516
Operand value = pop();
487517
Operand indexed = pop();
488-
code(new Instruction.Move(value, indexed));
518+
if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
519+
code(new Instruction.ArrayStore(value, loadIndexedOperand));
520+
}
521+
else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
522+
code(new Instruction.SetField(value, loadFieldOperand));
523+
}
524+
else
525+
code(new Instruction.Move(value, indexed));
489526
}
490527

491528
private boolean vstackEmpty() {

0 commit comments

Comments
 (0)