import { R8 } from "../sm83/cpu" import { Loc, LocType } from "../ir/loc" import type { Operand, SSA, SSABinary, SSACopy, SSAUnary } from "../ir/ssa" import type { RegAlloc } from "../regalloc" import { UnaryOp } from "../ast" export const generateBlock = (alloc: RegAlloc, block: Array): Array => { const output: Array = [] block.forEach(ssa => { output.push(...generateSSA(alloc, ssa)) }) return output } export const generateSSA = (alloc: RegAlloc, ssa: SSA): Array => { let output: Array = [] if ("source1" in ssa) { output = generateSSA_Binary(alloc, ssa) } else if ("op" in ssa) { output = generateSSA_Unary(alloc, ssa) } else { output = generateSSA_Copy(alloc, ssa) } return output } export const generateSSA_Copy = (alloc: RegAlloc, ssa: SSACopy): Array => { const output: Array = [] const source = typeof ssa.source === 'number' ? ssa.source.toString() : getAlloc(alloc, ssa.source).name switch (ssa.dest.type) { case LocType.TEMPORARY: const dest = getAlloc(alloc, ssa.dest) // TODO: Remove later with peephole optimizer if (dest.name !== source) { output.push(`LD ${dest.name}, ${source}`) } break case LocType.REGISTER: if (isA(ssa.dest) && typeof ssa.source !== 'number' && ssa.source.type === LocType.VARIABLE) { output.push(`LD A, (${ssa.source.name})`) // TODO: Remove check with peephole optimizer } else if (source !== ssa.dest.name) { output.push(`LD ${ssa.dest.name}, ${source}`) } break case LocType.VARIABLE: if (source !== R8.A) { console.log(source, ssa) throw new Error("unsupported") } output.push(`LD (${ssa.dest.name}), A`) break default: throw new Error(`unsupported destination type \`${ssa.dest.type}'`) } return output } const getAlloc = (alloc: RegAlloc, loc: Loc): Loc => { const destAlloc = alloc[loc.toString()] if (typeof destAlloc === "object") { throw new Error("stack variables not yet supported") } return Loc.reg(destAlloc) } export const generateSSA_Unary = (alloc: RegAlloc, ssa: SSAUnary): Array => { if (!isA(ssa.dest)) { throw new Error("Unexpected form for unary operation") } const output: Array = [] if (typeof ssa.source === "number") { output.push(`LD A, ${ssa.source}`) } else if (ssa.source.type === LocType.REGISTER) { if (!isA(ssa.dest)) { output.push(`LD A, ${ssa.source.name}`) } } else { // TODO: ?? output.push(`LD A, ${alloc[ssa.source.toString()]}`) } const ops = unaryOps[ssa.op] if (!ops) { throw new Error(`unsupported unary op \`${ssa.op}'`) } return output.concat(ops) } const unaryOps = { "arith_negate": ["CPL", "INC A"], "bit_negate": ["CPL"] } export const generateSSA_Binary = (alloc: RegAlloc, ssa: SSABinary): Array => { if (!isA(ssa.dest) || !isA(ssa.source)) { throw new Error("Unexpected form for binary operation") } const output: Array = [] const source = typeof ssa.source1 === 'number' ? ssa.source1 : getAlloc(alloc, ssa.source1).name switch (ssa.op) { case "add": output.push(`ADD ${source}`) break default: throw new Error(`unsupported binary op \`${ssa.op}'`) } return output } const isA = (loc: Operand): boolean => typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A