From 501cd997e91b6228505141062ebc68bbd350bf0a Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Tue, 10 Dec 2024 15:45:19 +0100 Subject: [PATCH] add assembler --- compiler/Lowerer.ts | 31 ++++++++---- compiler/program_builder.ts | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 compiler/program_builder.ts diff --git a/compiler/Lowerer.ts b/compiler/Lowerer.ts index c7525a5..3557730 100644 --- a/compiler/Lowerer.ts +++ b/compiler/Lowerer.ts @@ -2,10 +2,11 @@ import { Builtins } from "./arch.ts"; import { BinaryType, Expr, Stmt } from "./ast.ts"; import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts"; import { Ops } from "./mod.ts"; +import { Assembler } from "./program_builder.ts"; import { VType, vtypeToString } from "./vtypes.ts"; export class Lowerer { - private program: number[] = []; + private program = new Assembler(); private locals: Locals = new LocalsFnRoot(); private fnStmtIdAddrMap: { [key: number]: number } = {}; @@ -16,7 +17,7 @@ export class Lowerer { } public finish(): number[] { - return this.program; + return this.program.assemble(); } private lowerStaticStmt(stmt: Stmt) { @@ -60,7 +61,7 @@ export class Lowerer { const outerLocals = this.locals; this.locals = new LocalsFnRoot(outerLocals); const outerProgram = this.program; - this.program = []; + this.program = new Assembler(); for (const { ident } of stmt.kind.params) { this.locals.allocSym(ident); @@ -73,7 +74,7 @@ export class Lowerer { this.program.push(Ops.Return); this.locals = outerLocals; - outerProgram.push(...this.program); + outerProgram.concat(this.program); this.program = outerProgram; } @@ -222,13 +223,25 @@ export class Lowerer { if (expr.kind.type !== "if") { throw new Error(); } + + const falseLabel = this.program.makeLabel(); + const doneLabel = this.program.makeLabel(); + this.lowerExpr(expr.kind.cond); + + this.program.push(Ops.Not, Ops.JumpIfTrue); + this.program.addRef(falseLabel); + this.lowerExpr(expr.kind.truthy); - const falsyIndex = this.program.length; - if (expr.kind.falsy) { - this.lowerExpr(expr.kind.falsy); - } - const doneIndex = this.program.length; + + this.program.push(Ops.Jump); + this.program.addRef(doneLabel); + + this.program.setLabel(falseLabel); + + this.lowerExpr(expr.kind.falsy!); + + this.program.setLabel(doneLabel); } private lowerBlockExpr(expr: Expr) { diff --git a/compiler/program_builder.ts b/compiler/program_builder.ts new file mode 100644 index 0000000..83097e9 --- /dev/null +++ b/compiler/program_builder.ts @@ -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; + } +}