import { R8 } from "../sm83/cpu" import { Loc, LocType } from "../ir/loc" import type { Operand, AbsInsn2, AbsInsnCopy, AbsInsn2Unary } from "../ir/insn" import type { RegAlloc } from "../regalloc" 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: AbsInsn2): Array => { let output: Array = [] switch (ssa.type) { case 'copy': output = generateSSA_Copy(alloc, ssa) break case 'unary': output = generateSSA_Unary(alloc, ssa) break } return output } export const generateSSA_Copy = (alloc: RegAlloc, ssa: AbsInsnCopy): 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 later with peephole optimizer } else if (source !== ssa.dest.name) { output.push(`LD ${ssa.dest.name}, ${source}`) } break case LocType.VARIABLE: if (source !== R8.A) { throw new Error(`can only deref address into A (had ${source})`) } 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: AbsInsn2Unary): Array => { if (!isA(ssa.dest)) { throw new Error("Unexpected form for unary operation") } const source = typeof ssa.source === 'number' ? ssa.source.toString() : getAlloc(alloc, ssa.source).name switch (ssa.op) { case "add": return [`ADD ${source}`] case "arith_negate": return ["CPL", "INC A"] case "bit_negate": return ["CPL"] default: throw new Error(`unsupported unary op \`${ssa.op}'`) } } const isA = (loc: Operand): boolean => typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A