Author | SHA1 | Message | Date |
---|---|---|---|
Forest Belton | 19d01547fb | Support remaining operators from parser | 3 years ago |
Forest Belton | 999595fcd2 | Factor out register assignment from code generation | 3 years ago |
Forest Belton | 121337160f | Improve error message | 3 years ago |
@ -0,0 +1,33 @@ | |||||
import type { RegAlloc } from '../regalloc' | |||||
import { AbsInsn2, CopyInsn, insnReduce2, Operand, UnaryInsn } from '../ir/insn' | |||||
import { Loc, LocType } from '../ir/loc' | |||||
import { UnaryOp, BinaryOp } from '../ast' | |||||
export const allocate = (alloc: RegAlloc, block: Array<AbsInsn2>): Array<AbsInsn2> => | |||||
block.map(insn => allocateInsn(alloc, insn)) | |||||
export const allocateInsn = (alloc: RegAlloc, insn: AbsInsn2): AbsInsn2 => insnReduce2<AbsInsn2>( | |||||
(dest: Loc, source: Operand) => CopyInsn( | |||||
allocateInsnDest(alloc, dest), | |||||
typeof source === 'number' ? source : allocateInsnDest(alloc, source), | |||||
), | |||||
(dest: Loc, op: UnaryOp | BinaryOp, source: Operand) => UnaryInsn( | |||||
allocateInsnDest(alloc, dest), | |||||
op, | |||||
typeof source === 'number' ? source : allocateInsnDest(alloc, source), | |||||
), | |||||
insn, | |||||
) | |||||
export const allocateInsnDest = (alloc: RegAlloc, loc: Loc): Loc => | |||||
loc.type === LocType.TEMPORARY | |||||
? lookupAllocation(alloc, loc.ppName()) | |||||
: loc | |||||
const lookupAllocation = (alloc: RegAlloc, name: string): Loc => { | |||||
const loc = alloc[name] | |||||
if (typeof loc === 'object') { | |||||
throw new Error('stack offsets not supported yet') | |||||
} | |||||
return Loc.reg(loc) | |||||
} |
@ -1,107 +0,0 @@ | |||||
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<AbsInsn2>): Array<string> => { | |||||
const output: Array<string> = [] | |||||
block.forEach(ssa => { | |||||
output.push(...generateSSA(alloc, ssa)) | |||||
}) | |||||
return output | |||||
} | |||||
export const generateSSA = (alloc: RegAlloc, ssa: AbsInsn2): Array<string> => { | |||||
let output: Array<string> = [] | |||||
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<string> => { | |||||
const output: Array<string> = [] | |||||
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: AbsInsn2Unary): Array<string> => { | |||||
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 |
@ -0,0 +1 @@ | |||||
export type SM83Insn = string |
@ -0,0 +1,63 @@ | |||||
import { R8 } from "./cpu" | |||||
import { Loc, LocType } from "../ir/loc" | |||||
import { Operand, AbsInsn2, insnReduce2 } from "../ir/insn" | |||||
import { BinaryOp, UnaryOp } from "../ast" | |||||
import type { SM83Insn } from "./insn" | |||||
export const realizeBlock = (block: Array<AbsInsn2>): Array<SM83Insn> => block.flatMap(realizeInsn) | |||||
export const realizeInsn = (insn: AbsInsn2): Array<SM83Insn> => insnReduce2(realizeCopy, realizeUnary, insn) | |||||
const getSourceName = (source: Operand): string => typeof source === "number" | |||||
? source.toString() | |||||
: source.asmName() | |||||
export const realizeCopy = (dest: Loc, source: Operand): Array<SM83Insn> => [ | |||||
`LD ${dest.asmName()}, ${getSourceName(source)}` | |||||
] | |||||
export const realizeUnary = (dest: Loc, op: UnaryOp | BinaryOp, source: Operand): Array<SM83Insn> => { | |||||
if (!isA(dest)) { | |||||
throw new Error("unexpected form for unary operation") | |||||
} | |||||
let output: Array<SM83Insn> = [] | |||||
switch (op) { | |||||
case "add": | |||||
return [`ADD ${getSourceName(source)}`] | |||||
case "subtract": | |||||
return [`SUB ${getSourceName(source)}`] | |||||
case "bit_and": | |||||
return [`AND ${getSourceName(source)}`] | |||||
case "bit_or": | |||||
return [`OR ${getSourceName(source)}`] | |||||
case "bit_xor": | |||||
return [`XOR ${getSourceName(source)}`] | |||||
case "shift_left": | |||||
for (let i = 0; i < source; ++i) { | |||||
output.push("SLA A") | |||||
} | |||||
return output | |||||
case "shift_right": | |||||
for (let i = 0; i < source; ++i) { | |||||
output.push("SRL A") | |||||
} | |||||
return output | |||||
case "arith_negate": | |||||
return ["CPL", "INC A"] | |||||
case "bit_negate": | |||||
return ["CPL"] | |||||
} | |||||
} | |||||
const isA = (loc: Operand): boolean => | |||||
typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A |