diff --git a/lib/asm.ts b/lib/asm.ts deleted file mode 100644 index 5553ebc..0000000 --- a/lib/asm.ts +++ /dev/null @@ -1,240 +0,0 @@ -import type { SSA, SSABinary, SSACopy, SSAUnary } from "./ir" - -const reg8 = ["B", "C", "D", "E"] as const - -type R8 = typeof reg8[number] - -class ASMState { - regs: { [r in R8]: string | null } - syms: { [s: string]: R8 } - stack_offsets: { [s: string]: number } - cur_offset: number - insns: Array - - constructor() { - this.regs = { "B": null, "C": null, "D": null, "E": null } - this.syms = {} - this.stack_offsets = {} - this.cur_offset = 0 - this.insns = [] - } - - // TODO: Generalize for u16 - get_sym_loc(sym: string): R8 | number | null { - // return register if already allocated - if (typeof this.syms[sym] !== "undefined") { - return this.syms[sym] - } - - // return stack offset if already allocated - if (typeof this.stack_offsets[sym] !== "undefined") { - return this.cur_offset - this.stack_offsets[sym] - } - - return null - } - - alloc_sym_loc(sym: string): R8 | number { - const existing_loc = this.get_sym_loc(sym) - if (existing_loc !== null) { - return existing_loc - } - - // look for free register - for (const r8 of reg8) { - if (this.regs[r8] === null) { - this.regs[r8] = sym - this.syms[sym] = r8 - return r8 - } - } - - // allocate space on stack - this.cur_offset++ - this.stack_offsets[sym] = this.cur_offset - - this.insns.push("ADD SP, -1") - return this.stack_offsets[sym] - } -} - -export const convertASM = (ir: Array): Array => { - const state = new ASMState() - - ir.forEach(ssa => convertASM_SSA(state, ssa)) - - for (let sym of Object.keys(state.syms)) { - if (sym.startsWith("temp")) { - continue - } - - state.insns.push( - `LD A, ${state.syms[sym]}`, - `LD (${sym}), A` - ) - } - - for (let sym of Object.keys(state.stack_offsets)) { - if (sym.startsWith("temp")) { - continue - } - - state.insns.push( - `LD HL, SP + (${state.cur_offset - state.stack_offsets[sym]})`, - `LD A, (HL)`, - `LD (${sym}), A` - ) - } - - if (state.cur_offset !== 0) { - state.insns.push(`ADD SP, ${state.cur_offset}`) - } - - return state.insns -} - -// TODO: Track liveness -const convertASM_SSA = (state: ASMState, ssa: SSA): void => { - if ("source1" in ssa) { // Binary - convertASM_SSA_Binary(state, ssa) - } else if ("op" in ssa) { // Unary - convertASM_SSA_Unary(state, ssa) - } else { // Copy - convertASM_SSA_Copy(state, ssa) - } -} - -export const convertASM_SSA_Binary = (state: ASMState, ssa: SSABinary): void => { - const dest = state.alloc_sym_loc(ssa.dest) - - if (typeof ssa.source === "number") { - state.insns.push(`LD A, ${ssa.source}`) - } else { - const loc = state.get_sym_loc(ssa.source) - - if (loc === null) { - state.insns.push(`LD A, (${ssa.source})`) - } else if (typeof loc === "string") { - state.insns.push(`LD A, ${loc}`) - } else { - state.insns.push( - `LD HL, SP + (${loc})`, - `LD A, (HL)` - ) - } - } - - switch (ssa.op) { - case "add": - if (typeof ssa.source1 === "number") { - state.insns.push(`ADD ${ssa.source1}`) - } else { - const loc = state.get_sym_loc(ssa.source1) - if (loc === null) { - throw new Error('fuck') - } else if (typeof loc === "string") { - state.insns.push(`ADD ${loc}`) - } else { - throw new Error('fuck') - state.insns.push( - `LD HL, SP + (${loc})` - ) - } - } - break - - default: - throw new Error(`unsupported binary op \`${ssa.op}'`) - } - - if (typeof dest === "string") { - state.insns.push(`LD ${dest}, A`) - } else { - state.insns.push( - `LD HL, SP + (${dest})`, - `LD (HL), A` - ) - } -} - -export const convertASM_SSA_Unary = (state: ASMState, ssa: SSAUnary): void => { - const dest = state.alloc_sym_loc(ssa.dest) - - if (typeof ssa.source == "number") { - state.insns.push(`LD A, ${ssa.source}`) - } else { - const loc = state.get_sym_loc(ssa.source) - - if (loc === null) { - state.insns.push(`LD A, (${ssa.source})`) - } else if (typeof loc === "string") { - state.insns.push(`LD A, ${loc}`) - } else { - state.insns.push( - `LD HL, SP + (${loc})`, - `LD A, (HL)` - ) - } - } - - switch (ssa.op) { - case 'arith_negate': - state.insns.push( - `CPL`, - `INC A`, - ) - break - - case 'bit_negate': - state.insns.push( - `CPL` - ) - break - - default: - throw new Error(`unsupported unary op \`'${ssa.op}`) - } - - if (typeof dest === "string") { - state.insns.push(`LD ${dest}, A`) - } else { - state.insns.push( - `LD HL, SP + (${dest})`, - `LD (HL), A` - ) - } -} - - -export const convertASM_SSA_Copy = (state: ASMState, ssa: SSACopy): void => { - const dest = state.alloc_sym_loc(ssa.dest) - - let source = "" - if (typeof ssa.source == "number") { - source = ssa.source.toString() - } else { - const loc = state.get_sym_loc(ssa.source) - - if (loc === null) { - state.insns.push(`LD A, (${ssa.source})`) - source = "A" - } else if (typeof loc === "string") { - source = loc - } else { - state.insns.push( - `LD HL, SP + (${loc})`, - `LD A, (HL)` - ) - source = "A" - } - } - - if (typeof dest === "string") { - state.insns.push(`LD ${dest}, ${source}`) - } else { - state.insns.push( - `LD HL, SP + (${dest})`, - `LD (HL), ${source}` - ) - } -} diff --git a/lib/ast.d.ts b/lib/ast.d.ts index b4a5ce3..6f87d58 100644 --- a/lib/ast.d.ts +++ b/lib/ast.d.ts @@ -34,19 +34,23 @@ export type Expr = BinaryExpr | UnaryExpr | BasicExpr type BinaryExpr = { type: "binary", - op: string, + op: BinaryOp, left: Expr, right: Expr, } type UnaryExpr = { type: "unary", - op: string, + op: UnaryOp, arg: Expr, } type BasicExpr = number | string +type UnaryOp = "arith_negate" | "bit_negate" +type BinaryOp = "add" | "subtract" | "shift_left" | "shift_right" | "bit_and" + | "bit_xor" | "bit_or" + // Attributes export type Attr = { name: string, diff --git a/lib/compile.ts b/lib/compile.ts index 733022e..7093b9b 100644 --- a/lib/compile.ts +++ b/lib/compile.ts @@ -1,6 +1,7 @@ -import { convertASM } from "./asm" +// import { convertASM } from "./asm" import { convertIR, prettyPrintIR } from "./ir" import { parse } from "./parser" +import { inspect } from "util" import type { Attr, Decl, Stmt, Type, VarDecl } from "./ast" @@ -38,22 +39,24 @@ export const compile = (fileName: string, source: string): string => { console.log(ir.map(prettyPrintIR).join("\n")) // 5. Generate code - const insns = convertASM(ir) - - let output = '' - if (asmDecls.length > 0) { - output += `SECTION "${fileName} Data", WRAM0\n\n` - output += asmDecls.join("\n") - output += "\n\n" - } - - if (insns.length > 0) { - output += `SECTION "${fileName} Code", ROM0\n\n` - output += insns.join("\n") - } - - console.log("=== ASM === ") - return output + /* const insns = convertASM(ir) + + let output = '' + if (asmDecls.length > 0) { + output += `SECTION "${fileName} Data", WRAM0\n\n` + output += asmDecls.join("\n") + output += "\n\n" + } + + if (insns.length > 0) { + output += `SECTION "${fileName} Code", ROM0\n\n` + output += insns.join("\n") + } + + console.log("=== ASM === ") + return output */ + + return '' } export const processDecls = (decls: Array): SymbolMap => decls.reduce(processDecl, {}) diff --git a/lib/graph/graph.ts b/lib/graph/graph.ts index 5ecf1ee..8ecb880 100644 --- a/lib/graph/graph.ts +++ b/lib/graph/graph.ts @@ -48,7 +48,7 @@ export const maxCardinalitySearch = (g: Graph): Array => { const weights: { [s: string]: number } = {} const ordering: Array = [] - let W = vertices(g) + const W = vertices(g) const numVertices = W.size for (let i = 0; i < numVertices; ++i) { diff --git a/lib/ir.ts b/lib/ir.ts index 5f2c84f..65cbb3b 100644 --- a/lib/ir.ts +++ b/lib/ir.ts @@ -1,14 +1,21 @@ -import type { AssignStmt, Expr, Stmt } from "./ast" +import type { AssignStmt, Expr, Stmt, UnaryOp, BinaryOp } from "./ast" -type Operand = string | number +type LocationType = "register" | "temporary" | "variable" -type ASSA = { dest: string, source: Operand } & Data +type Location = { + type: LocationType, + name: string, +} + +type Operand = Location | number -export type SSACopy = ASSA<{ source: Operand }> // dest = source +type ASSA = { dest: Location, source: Operand } & Data -export type SSAUnary = ASSA<{ op: string }> // dest = op(source) +export type SSACopy = ASSA<{}> // dest = source -export type SSABinary = ASSA<{ op: string, source1: Operand }> // dest = op(source, source1) +export type SSAUnary = ASSA<{ op: UnaryOp }> // dest = op(source) + +export type SSABinary = ASSA<{ op: BinaryOp, source1: Operand }> // dest = op(source, source1) export type SSA = SSACopy | SSAUnary | SSABinary @@ -17,6 +24,21 @@ type IRState = { ssa_stmts: Array } +const temp = (state: IRState): Location => ({ + type: "temporary", + name: `t${state.nextID++}`, +}) + +const vari = (name: string): Location => ({ + type: "variable", + name, +}) + +const reg = (name: string): Location => ({ + type: "register", + name, +}) + export const convertIR = (stmts: Array): Array => { const state: IRState = { nextID: 0, @@ -32,14 +54,16 @@ export const convertIR = (stmts: Array): Array => { } export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array => { - return convertExpr(state, stmt.args.name, stmt.args.expr) + return convertExpr(state, vari(stmt.args.name), stmt.args.expr) } -export const convertExpr = (state: IRState, dest: string, expr: Expr): Array => { +export const convertExpr = (state: IRState, dest: Location, expr: Expr): Array => { let expr_stmts = [] - if (typeof expr === "number" || typeof expr === "string") { + if (typeof expr === "number") { expr_stmts = [{ dest, source: expr }] + } else if (typeof expr === "string") { + expr_stmts = [{ dest, source: vari(expr) }] } else if (expr.type === "unary") { const [source, stmts] = getSource(state, expr.arg) stmts.push({ @@ -67,12 +91,14 @@ export const convertExpr = (state: IRState, dest: string, expr: Expr): Array] => { - if (typeof expr === "number" || typeof expr === "string") { +const getSource = (state: IRState, expr: Expr): [Operand, Array] => { + if (typeof expr === "number") { return [expr, []] + } else if (typeof expr === "string") { + return [vari(expr), []] } - const source = `temp${state.nextID++}` + const source = temp(state) const stmts = convertExpr(state, source, expr) return [source, stmts] } @@ -81,11 +107,36 @@ export const prettyPrintIR = (ssa: SSA): string => { let output = "" if ("source1" in ssa) { - output = `${ssa.dest} = ${ssa.op}(${ssa.source}, ${ssa.source1})` + output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)}, ${ppOp(ssa.source1)})` } else if ("op" in ssa) { - output = `${ssa.dest} = ${ssa.op}(${ssa.source})` + output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)})` } else { - output = `${ssa.dest} = ${ssa.source}` + output = `${ppLoc(ssa.dest)} = ${ppOp(ssa.source)}` + } + + return output +} + +const ppOp = (op: Operand): string => + typeof op === "number" + ? op.toString() + : ppLoc(op) + +const ppLoc = (loc: Location): string => { + let output = '' + + switch (loc.type) { + case "register": + output = `%${loc.name}` + break + + case "temporary": + output = `#${loc.name}` + break + + case "variable": + output = loc.name + break } return output