add assembler

This commit is contained in:
SimonFJ20 2024-12-10 15:45:19 +01:00
parent bf9c0aa866
commit 501cd997e9
2 changed files with 119 additions and 9 deletions

View File

@ -2,10 +2,11 @@ import { Builtins } from "./arch.ts";
import { BinaryType, Expr, Stmt } from "./ast.ts"; import { BinaryType, Expr, Stmt } from "./ast.ts";
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts"; import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
import { Ops } from "./mod.ts"; import { Ops } from "./mod.ts";
import { Assembler } from "./program_builder.ts";
import { VType, vtypeToString } from "./vtypes.ts"; import { VType, vtypeToString } from "./vtypes.ts";
export class Lowerer { export class Lowerer {
private program: number[] = []; private program = new Assembler();
private locals: Locals = new LocalsFnRoot(); private locals: Locals = new LocalsFnRoot();
private fnStmtIdAddrMap: { [key: number]: number } = {}; private fnStmtIdAddrMap: { [key: number]: number } = {};
@ -16,7 +17,7 @@ export class Lowerer {
} }
public finish(): number[] { public finish(): number[] {
return this.program; return this.program.assemble();
} }
private lowerStaticStmt(stmt: Stmt) { private lowerStaticStmt(stmt: Stmt) {
@ -60,7 +61,7 @@ export class Lowerer {
const outerLocals = this.locals; const outerLocals = this.locals;
this.locals = new LocalsFnRoot(outerLocals); this.locals = new LocalsFnRoot(outerLocals);
const outerProgram = this.program; const outerProgram = this.program;
this.program = []; this.program = new Assembler();
for (const { ident } of stmt.kind.params) { for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident); this.locals.allocSym(ident);
@ -73,7 +74,7 @@ export class Lowerer {
this.program.push(Ops.Return); this.program.push(Ops.Return);
this.locals = outerLocals; this.locals = outerLocals;
outerProgram.push(...this.program); outerProgram.concat(this.program);
this.program = outerProgram; this.program = outerProgram;
} }
@ -222,13 +223,25 @@ export class Lowerer {
if (expr.kind.type !== "if") { if (expr.kind.type !== "if") {
throw new Error(); throw new Error();
} }
const falseLabel = this.program.makeLabel();
const doneLabel = this.program.makeLabel();
this.lowerExpr(expr.kind.cond); this.lowerExpr(expr.kind.cond);
this.program.push(Ops.Not, Ops.JumpIfTrue);
this.program.addRef(falseLabel);
this.lowerExpr(expr.kind.truthy); this.lowerExpr(expr.kind.truthy);
const falsyIndex = this.program.length;
if (expr.kind.falsy) { this.program.push(Ops.Jump);
this.lowerExpr(expr.kind.falsy); this.program.addRef(doneLabel);
}
const doneIndex = this.program.length; this.program.setLabel(falseLabel);
this.lowerExpr(expr.kind.falsy!);
this.program.setLabel(doneLabel);
} }
private lowerBlockExpr(expr: Expr) { private lowerBlockExpr(expr: Expr) {

View File

@ -0,0 +1,97 @@
import { Ops } from "./arch.ts";
export type Line = {
labels?: number[];
} & LineKind;
export type Label = { label: number };
export type LineKind =
| { type: "op"; op: number }
| { type: "lit"; val: number }
| { type: "ref"; label: number };
export class Assembler {
private lines: Line[] = [];
private labelCounter = 0;
public push(...values: number[]) {
for (const value of values) {
this.addLit(value);
}
}
public addOp(op: number): Assembler {
this.lines.push({ type: "op", op });
return this;
}
public addLit(val: number): Assembler {
this.lines.push({ type: "lit", val });
return this;
}
public addRef({ label }: Label): Assembler {
this.lines.push({ type: "ref", label });
return this;
}
public setLabel({ label }: Label): Assembler {
const line = this.lines.at(-1);
if (line === undefined) {
return this;
}
if (line.labels === undefined) {
line.labels = [];
}
line.labels.push(label);
return this;
}
public makeLabel(): Label {
const label = this.labelCounter;
this.labelCounter += 1;
return { label };
}
public concat(assembler: Assembler) {
this.lines.push(...assembler.lines);
}
public assemble(): number[] {
let ip = 0;
const output: number[] = [];
const locs: { [key: number]: number } = {};
const refs: { [key: number]: number } = {};
for (const line of this.lines) {
switch (line.type) {
case "op":
output.push(line.op);
ip += 1;
break;
case "lit":
output.push(line.val);
ip += 1;
break;
case "ref":
output.push(0);
refs[ip] = line.label;
ip += 1;
break;
}
}
for (let i = 0; i < output.length; ++i) {
if (!(i in refs)) {
continue;
}
if (!(refs[i] in locs)) {
console.error(
`Assembler: label '${refs[i]}' used at ${i} not defined`,
);
continue;
}
output[i] = locs[refs[i]];
}
return output;
}
}