mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 08:24:05 +02:00
compiler: operators and while loop
This commit is contained in:
parent
cb0406e180
commit
15d0892efd
@ -27,10 +27,25 @@ export class AsmGen {
|
||||
this.writeIns(`db "${escaped}"`);
|
||||
}
|
||||
this.writeln(`section .text`);
|
||||
|
||||
for (const fn of this.lir.fns) {
|
||||
this.generateFn(fn);
|
||||
}
|
||||
|
||||
this.writeln(`sbc__div:`);
|
||||
this.writeIns(`mov rdx, 0`);
|
||||
this.writeIns(`mov rax, [rsp+16]`);
|
||||
this.writeIns(`mov rdi, [rsp+8]`);
|
||||
this.writeIns(`div rdi`);
|
||||
this.writeIns(`ret`);
|
||||
this.writeln(`sbc__mod:`);
|
||||
this.writeIns(`mov rdx, 0`);
|
||||
this.writeIns(`mov rax, [rsp+16]`);
|
||||
this.writeIns(`mov rdi, [rsp+8]`);
|
||||
this.writeIns(`div rdi`);
|
||||
this.writeIns(`mov rax, rdx`);
|
||||
this.writeIns(`ret`);
|
||||
|
||||
this.writeln(`; vim: syntax=nasm commentstring=;\\ %s`);
|
||||
this.writeln("");
|
||||
return this.writer.finalize();
|
||||
@ -241,16 +256,47 @@ export class AsmGen {
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setl ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "gt":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setg ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "le":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setle ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "ge":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setge ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "eq":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`sete ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "ne":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setne ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "add":
|
||||
this.writeIns(`add ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
return;
|
||||
case "sub":
|
||||
this.writeIns(`sub ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
return;
|
||||
case "mul":
|
||||
this.writeIns(`imul ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
return;
|
||||
case "div":
|
||||
this.writeIns(`push ${r(ins.dst)}`);
|
||||
this.writeIns(`push ${r(ins.src)}`);
|
||||
this.writeIns(`call sbc__div`);
|
||||
this.writeIns(`mov ${r(ins.dst)}, rax`);
|
||||
return;
|
||||
case "mod":
|
||||
this.writeIns(`push ${r(ins.dst)}`);
|
||||
this.writeIns(`push ${r(ins.src)}`);
|
||||
this.writeIns(`call sbc__mod`);
|
||||
this.writeIns(`mov ${r(ins.dst)}, rax`);
|
||||
return;
|
||||
case "kill":
|
||||
this.kill(ins.reg);
|
||||
return;
|
||||
|
19
sbc/ast.ts
19
sbc/ast.ts
@ -16,6 +16,7 @@ export type StmtKind =
|
||||
| { tag: "fn" } & FnStmt
|
||||
| { tag: "let" } & LetStmt
|
||||
| { tag: "loop"; body: Block }
|
||||
| { tag: "while"; expr: Expr; body: Block }
|
||||
| { tag: "if"; expr: Expr; truthy: Block; falsy?: Block }
|
||||
| { tag: "return"; expr?: Expr }
|
||||
| { tag: "break" }
|
||||
@ -54,9 +55,25 @@ export type ExprKind =
|
||||
| { tag: "int"; val: number }
|
||||
| { tag: "str"; val: string }
|
||||
| { tag: "call"; expr: Expr; args: Expr[] }
|
||||
| { tag: "not"; expr: Expr }
|
||||
| { tag: "negate"; expr: Expr }
|
||||
| { tag: "binary"; op: BinaryOp; left: Expr; right: Expr };
|
||||
|
||||
export type BinaryOp = "<" | "==" | "+" | "*";
|
||||
export type BinaryOp =
|
||||
| "or"
|
||||
| "xor"
|
||||
| "and"
|
||||
| "<"
|
||||
| ">"
|
||||
| "<="
|
||||
| ">="
|
||||
| "=="
|
||||
| "!="
|
||||
| "+"
|
||||
| "-"
|
||||
| "*"
|
||||
| "/"
|
||||
| "%";
|
||||
|
||||
export type Ty = {
|
||||
id: number;
|
||||
|
83
sbc/front.ts
83
sbc/front.ts
@ -140,24 +140,27 @@ export class Checker {
|
||||
}
|
||||
return callee.returnTy;
|
||||
}
|
||||
case "not":
|
||||
case "negate":
|
||||
throw new Error("todo");
|
||||
case "binary": {
|
||||
const left = this.exprTy(k.left);
|
||||
const right = this.exprTy(k.right);
|
||||
|
||||
const cfg = (op: BinaryOp, l: Ty, r: Ty = l) =>
|
||||
k.op === op && this.assignable(left, l) &&
|
||||
const cfg = (ops: BinaryOp[], l: Ty, r: Ty = l) =>
|
||||
ops.includes(k.op) && this.assignable(left, l) &&
|
||||
this.assignable(right, r);
|
||||
|
||||
if (cfg("<", { tag: "int" })) {
|
||||
if (cfg(["<", ">", "<=", ">="], { tag: "int" })) {
|
||||
return { tag: "int" };
|
||||
}
|
||||
if (cfg("==", { tag: "int" })) {
|
||||
if (cfg(["==", "!="], { tag: "int" })) {
|
||||
return { tag: "int" };
|
||||
}
|
||||
if (cfg("+", { tag: "int" })) {
|
||||
if (cfg(["+", "-"], { tag: "int" })) {
|
||||
return { tag: "int" };
|
||||
}
|
||||
if (cfg("*", { tag: "int" })) {
|
||||
if (cfg(["*", "/", "%"], { tag: "int" })) {
|
||||
return { tag: "int" };
|
||||
}
|
||||
|
||||
@ -436,6 +439,12 @@ export class Resolver {
|
||||
this.resolveBlock(k.body);
|
||||
this.loopStack.pop();
|
||||
return;
|
||||
case "while":
|
||||
this.resolveExpr(k.expr);
|
||||
this.loopStack.push(stmt);
|
||||
this.resolveBlock(k.body);
|
||||
this.loopStack.pop();
|
||||
return;
|
||||
case "if":
|
||||
this.resolveExpr(k.expr);
|
||||
this.resolveBlock(k.truthy);
|
||||
@ -487,6 +496,9 @@ export class Resolver {
|
||||
this.resolveExpr(arg);
|
||||
}
|
||||
return;
|
||||
case "not":
|
||||
case "negate":
|
||||
throw new Error("todo");
|
||||
case "binary":
|
||||
this.resolveExpr(k.left);
|
||||
this.resolveExpr(k.right);
|
||||
@ -560,6 +572,8 @@ export class Parser {
|
||||
return this.parseLetStmt();
|
||||
} else if (this.test("loop")) {
|
||||
return this.parseLoopStmt();
|
||||
} else if (this.test("while")) {
|
||||
return this.parseWhileStmt();
|
||||
} else if (this.test("if")) {
|
||||
return this.parseIfStmt();
|
||||
} else if (this.test("return")) {
|
||||
@ -732,6 +746,18 @@ export class Parser {
|
||||
return this.stmt({ tag: "loop", body }, line);
|
||||
}
|
||||
|
||||
private parseWhileStmt(): Stmt {
|
||||
const line = this.curr().line;
|
||||
this.step();
|
||||
const expr = this.parseExpr();
|
||||
if (!this.test("{")) {
|
||||
this.report("expected block");
|
||||
return this.stmt({ tag: "error" }, line);
|
||||
}
|
||||
const body = this.parseBlock();
|
||||
return this.stmt({ tag: "while", expr, body }, line);
|
||||
}
|
||||
|
||||
private parseIfStmt(): Stmt {
|
||||
const line = this.curr().line;
|
||||
this.step();
|
||||
@ -780,15 +806,25 @@ export class Parser {
|
||||
return this.parseBinaryExpr();
|
||||
}
|
||||
|
||||
private parseBinaryExpr(prec = 4): Expr {
|
||||
private parseBinaryExpr(prec = 7): Expr {
|
||||
if (prec == 0) {
|
||||
return this.parsePostfixExpr();
|
||||
return this.parsePrefixExpr();
|
||||
}
|
||||
const ops: [BinaryOp, number][] = [
|
||||
["or", 7],
|
||||
["xor", 6],
|
||||
["and", 5],
|
||||
["<", 4],
|
||||
[">", 4],
|
||||
["<=", 4],
|
||||
[">=", 4],
|
||||
["==", 3],
|
||||
["!=", 3],
|
||||
["+", 2],
|
||||
["-", 2],
|
||||
["*", 1],
|
||||
["/", 1],
|
||||
["%", 1],
|
||||
];
|
||||
|
||||
let left = this.parseBinaryExpr(prec - 1);
|
||||
@ -811,6 +847,18 @@ export class Parser {
|
||||
return left;
|
||||
}
|
||||
|
||||
private parsePrefixExpr(): Expr {
|
||||
if (this.eat("not")) {
|
||||
const expr = this.parsePrefixExpr();
|
||||
return this.expr({ tag: "not", expr }, this.eaten!.line);
|
||||
} else if (this.eat("-")) {
|
||||
const expr = this.parsePrefixExpr();
|
||||
return this.expr({ tag: "negate", expr }, this.eaten!.line);
|
||||
} else {
|
||||
return this.parsePostfixExpr();
|
||||
}
|
||||
}
|
||||
|
||||
private parsePostfixExpr(): Expr {
|
||||
let expr = this.parseOperandExpr();
|
||||
while (true) {
|
||||
@ -933,8 +981,21 @@ export type Tok = {
|
||||
};
|
||||
|
||||
export function lex(text: string): Tok[] {
|
||||
const ops = "(){}[]<>+-*=:,;#\n";
|
||||
const kws = ["let", "fn", "return", "if", "else", "loop", "break"];
|
||||
const ops = "(){}[]<>+-*/%=!:,;#\n";
|
||||
const kws = [
|
||||
"let",
|
||||
"fn",
|
||||
"return",
|
||||
"if",
|
||||
"else",
|
||||
"loop",
|
||||
"while",
|
||||
"break",
|
||||
"not",
|
||||
"or",
|
||||
"xor",
|
||||
"and",
|
||||
];
|
||||
|
||||
return ops
|
||||
.split("")
|
||||
@ -943,6 +1004,8 @@ export function lex(text: string): Tok[] {
|
||||
.replaceAll(/\/\/.*?$/mg, "")
|
||||
.replaceAll(op, ` ${op} `)
|
||||
.replaceAll(" - > ", " -> ")
|
||||
.replaceAll(" < = ", " <= ")
|
||||
.replaceAll(" > = ", " >= ")
|
||||
.replaceAll(" = = ", " == ")
|
||||
.replaceAll(/\\ /g, "\\SPACE"), text)
|
||||
.split(/[ \t\r]/)
|
||||
|
22
sbc/lir.ts
22
sbc/lir.ts
@ -37,9 +37,22 @@ export type Ins =
|
||||
| { tag: "jmp"; target: Label }
|
||||
| { tag: "jnz_reg"; reg: Reg; target: Label }
|
||||
| { tag: "ret" }
|
||||
| { tag: "lt" | "eq" | "add" | "mul"; dst: Reg; src: Reg }
|
||||
| { tag: BinaryOp; dst: Reg; src: Reg }
|
||||
| { tag: "kill"; reg: Reg };
|
||||
|
||||
export type BinaryOp =
|
||||
| "lt"
|
||||
| "gt"
|
||||
| "le"
|
||||
| "ge"
|
||||
| "eq"
|
||||
| "ne"
|
||||
| "add"
|
||||
| "sub"
|
||||
| "mul"
|
||||
| "div"
|
||||
| "mod";
|
||||
|
||||
export type Reg = number;
|
||||
export type Label = number;
|
||||
|
||||
@ -103,9 +116,16 @@ export class ProgramStringifyer {
|
||||
case "ret":
|
||||
return "ret";
|
||||
case "lt":
|
||||
case "gt":
|
||||
case "le":
|
||||
case "ge":
|
||||
case "eq":
|
||||
case "ne":
|
||||
case "add":
|
||||
case "sub":
|
||||
case "div":
|
||||
case "mul":
|
||||
case "mod":
|
||||
return `${ins.tag} %${ins.dst}, %${ins.src}`;
|
||||
case "kill":
|
||||
return `kill %${ins.reg}`;
|
||||
|
@ -192,9 +192,16 @@ class FnGen {
|
||||
return;
|
||||
}
|
||||
case "lt":
|
||||
case "gt":
|
||||
case "le":
|
||||
case "ge":
|
||||
case "ne":
|
||||
case "eq":
|
||||
case "add":
|
||||
case "mul": {
|
||||
case "sub":
|
||||
case "mul":
|
||||
case "div":
|
||||
case "mod": {
|
||||
const src = this.reg();
|
||||
const dst = this.reg();
|
||||
this.pushIns({ tag: "pop", reg: src });
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
} from "./lir.ts";
|
||||
|
||||
export function optimizeLir(program: Program) {
|
||||
console.log("=== BEFORE OPTIMIZATION ===");
|
||||
console.log(new ProgramStringifyer(program).stringify());
|
||||
// console.log("=== BEFORE OPTIMIZATION ===");
|
||||
// console.log(new ProgramStringifyer(program).stringify());
|
||||
|
||||
let sizeBefore = program.fns
|
||||
.reduce((acc, fn) => acc + fn.lines.length, 0);
|
||||
@ -34,8 +34,8 @@ export function optimizeLir(program: Program) {
|
||||
sizeHistory.add(sizeBefore);
|
||||
}
|
||||
|
||||
console.log("=== AFTER OPTIMIZATION ===");
|
||||
console.log(new ProgramStringifyer(program).stringify());
|
||||
// console.log("=== AFTER OPTIMIZATION ===");
|
||||
// console.log(new ProgramStringifyer(program).stringify());
|
||||
}
|
||||
|
||||
function eliminatePushPop(fn: Fn) {
|
||||
@ -237,9 +237,16 @@ function replaceReg(fn: Fn, cand: Reg, replacement: Reg) {
|
||||
case "ret":
|
||||
break;
|
||||
case "lt":
|
||||
case "gt":
|
||||
case "le":
|
||||
case "ge":
|
||||
case "eq":
|
||||
case "ne":
|
||||
case "add":
|
||||
case "sub":
|
||||
case "mul":
|
||||
case "div":
|
||||
case "mod":
|
||||
ins.dst = r(ins.dst);
|
||||
ins.src = r(ins.src);
|
||||
break;
|
||||
@ -278,9 +285,16 @@ function pollutesStack(ins: Ins): boolean {
|
||||
case "ret":
|
||||
return false;
|
||||
case "lt":
|
||||
case "gt":
|
||||
case "le":
|
||||
case "ge":
|
||||
case "eq":
|
||||
case "ne":
|
||||
case "add":
|
||||
case "sub":
|
||||
case "mul":
|
||||
case "div":
|
||||
case "mod":
|
||||
return true;
|
||||
case "kill":
|
||||
return false;
|
||||
|
22
sbc/mir.ts
22
sbc/mir.ts
@ -38,7 +38,20 @@ export type StmtKind =
|
||||
| { tag: "load"; local: Local }
|
||||
| { tag: "store"; local: Local }
|
||||
| { tag: "call"; args: number }
|
||||
| { tag: "lt" | "eq" | "add" | "mul" };
|
||||
| { tag: BinaryOp };
|
||||
|
||||
export type BinaryOp =
|
||||
| "lt"
|
||||
| "gt"
|
||||
| "le"
|
||||
| "ge"
|
||||
| "eq"
|
||||
| "ne"
|
||||
| "add"
|
||||
| "sub"
|
||||
| "mul"
|
||||
| "div"
|
||||
| "mod";
|
||||
|
||||
export type Ter = {
|
||||
kind: TerKind;
|
||||
@ -108,9 +121,16 @@ export class FnStringifyer {
|
||||
case "call":
|
||||
return `call ${k.args}`;
|
||||
case "lt":
|
||||
case "gt":
|
||||
case "le":
|
||||
case "ge":
|
||||
case "eq":
|
||||
case "ne":
|
||||
case "add":
|
||||
case "sub":
|
||||
case "mul":
|
||||
case "div":
|
||||
case "mod":
|
||||
return k.tag;
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +94,8 @@ export class FnMirGen {
|
||||
}
|
||||
case "loop": {
|
||||
const entry = this.currentBlock;
|
||||
const exit = this.block();
|
||||
const loop = this.block();
|
||||
const exit = this.block();
|
||||
|
||||
entry.ter = Ter({ tag: "goto", target: loop });
|
||||
|
||||
@ -108,6 +108,31 @@ export class FnMirGen {
|
||||
this.currentBlock = exit;
|
||||
return;
|
||||
}
|
||||
case "while": {
|
||||
const entry = this.currentBlock;
|
||||
const cond = this.block();
|
||||
const loop = this.block();
|
||||
const exit = this.block();
|
||||
|
||||
entry.ter = Ter({ tag: "goto", target: cond });
|
||||
|
||||
this.currentBlock = cond;
|
||||
this.lowerExpr(k.expr);
|
||||
this.currentBlock.ter = Ter({
|
||||
tag: "if",
|
||||
truthy: loop,
|
||||
falsy: exit,
|
||||
});
|
||||
|
||||
this.loopExitBlocks.set(stmt.id, exit);
|
||||
|
||||
this.currentBlock = loop;
|
||||
this.lowerBlock(k.body);
|
||||
this.currentBlock.ter = Ter({ tag: "goto", target: cond });
|
||||
|
||||
this.currentBlock = exit;
|
||||
return;
|
||||
}
|
||||
case "if": {
|
||||
this.lowerExpr(k.expr);
|
||||
const entry = this.currentBlock;
|
||||
@ -250,22 +275,50 @@ export class FnMirGen {
|
||||
});
|
||||
return;
|
||||
}
|
||||
case "not":
|
||||
case "negate":
|
||||
throw new Error("todo");
|
||||
case "binary": {
|
||||
this.lowerExpr(k.left);
|
||||
this.lowerExpr(k.right);
|
||||
switch (k.op) {
|
||||
case "or":
|
||||
case "xor":
|
||||
case "and":
|
||||
throw new Error("todo");
|
||||
case "<":
|
||||
this.pushStmt({ tag: "lt" });
|
||||
return;
|
||||
case ">":
|
||||
this.pushStmt({ tag: "gt" });
|
||||
return;
|
||||
case "<=":
|
||||
this.pushStmt({ tag: "le" });
|
||||
return;
|
||||
case ">=":
|
||||
this.pushStmt({ tag: "ge" });
|
||||
return;
|
||||
case "==":
|
||||
this.pushStmt({ tag: "eq" });
|
||||
return;
|
||||
case "!=":
|
||||
this.pushStmt({ tag: "ne" });
|
||||
return;
|
||||
case "+":
|
||||
this.pushStmt({ tag: "add" });
|
||||
return;
|
||||
case "-":
|
||||
this.pushStmt({ tag: "sub" });
|
||||
return;
|
||||
case "*":
|
||||
this.pushStmt({ tag: "mul" });
|
||||
return;
|
||||
case "/":
|
||||
this.pushStmt({ tag: "div" });
|
||||
return;
|
||||
case "%":
|
||||
this.pushStmt({ tag: "mod" });
|
||||
return;
|
||||
}
|
||||
const __: never = k.op;
|
||||
return;
|
||||
|
@ -2,9 +2,9 @@ import * as ast from "./ast.ts";
|
||||
import { Block, Fn, FnStringifyer } from "./mir.ts";
|
||||
|
||||
export function optimizeMirFn(fn: Fn) {
|
||||
//console.log(`=== OPTIMIZING ${(fn.stmt.kind as ast.FnStmt).ident} ===`);
|
||||
//console.log("=== BEFORE OPTIMIZATION ===");
|
||||
//console.log(new FnStringifyer(fn).stringify());
|
||||
console.log(`=== OPTIMIZING ${(fn.stmt.kind as ast.FnStmt).ident} ===`);
|
||||
console.log("=== BEFORE OPTIMIZATION ===");
|
||||
console.log(new FnStringifyer(fn).stringify());
|
||||
|
||||
const blockSize = fn.blocks
|
||||
.map((block) => block.stmts.length)
|
||||
@ -27,8 +27,8 @@ export function optimizeMirFn(fn: Fn) {
|
||||
sizeHistory.add(sizeBefore);
|
||||
}
|
||||
|
||||
//console.log("=== AFTER OPTIMIZATION ===");
|
||||
//console.log(new FnStringifyer(fn).stringify());
|
||||
console.log("=== AFTER OPTIMIZATION ===");
|
||||
console.log(new FnStringifyer(fn).stringify());
|
||||
}
|
||||
|
||||
function fnSize(fn: Fn, blockSize: number): number {
|
||||
|
@ -1,29 +1,16 @@
|
||||
|
||||
#[c_function("notice")]
|
||||
fn notice() -> int {}
|
||||
|
||||
#[c_function("print_int")]
|
||||
fn print_int(value: int) -> int {}
|
||||
|
||||
|
||||
#[c_function("println")]
|
||||
fn println(value: *str) -> int {}
|
||||
|
||||
fn add(a: int, b: int) -> int { return a + b; }
|
||||
fn compute() -> int { return 0; }
|
||||
fn consume(value: int) -> int { return 0; }
|
||||
|
||||
#[c_function("print_int")]
|
||||
fn print_int(value: int) -> int {}
|
||||
|
||||
#[c_export("sbc_main")]
|
||||
fn main() -> int {
|
||||
let i = 0;
|
||||
|
||||
loop {
|
||||
if 10 < i + 1 {
|
||||
break;
|
||||
}
|
||||
while i < 10 {
|
||||
|
||||
println("hello\ world");
|
||||
println("Hello\ world");
|
||||
|
||||
i = i + 1;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user