import type { AssignStmt, Expr, Stmt } from "../ast" import { R8 } from "../sm83/cpu" import { Loc, LocType } from "./loc" import { Operand, AbsInsn2, AbsInsn3, CopyInsn, UnaryInsn, BinaryInsn } from "./insn" export class IRState { nextID: number insns: Array constructor() { this.nextID = 0 this.insns = [] } temp(): Loc { return Loc.temp(`t${this.nextID++}`) } } export const convertIR = (stmts: Array): Array => { const state: IRState = new IRState() stmts.forEach(stmt => { convertAssignStmt(state, stmt) }) return state.insns } export const convertAssignStmt = (state: IRState, stmt: AssignStmt): void => { convertExpr(state, Loc.vari(stmt.args.name), stmt.args.expr) } export const convertExpr = (state: IRState, dest: Loc, expr: Expr): void => { const threeAddress = convertExpr3(state, dest, expr) threeAddress.forEach(ssa => { switch (ssa.type) { case 'copy': state.insns.push(ssa) break case 'unary': state.insns.push( CopyInsn(Loc.reg(R8.A), ssa.source), UnaryInsn(Loc.reg(R8.A), ssa.op, Loc.reg(R8.A)), CopyInsn(ssa.dest, Loc.reg(R8.A)), ) break case 'binary': let source1 = ssa.source1 if (typeof source1 !== 'number' && source1.type === LocType.VARIABLE) { source1 = state.temp() state.insns.push( CopyInsn(Loc.reg(R8.A), ssa.source1), CopyInsn(source1, Loc.reg(R8.A)), ) } state.insns.push( CopyInsn(Loc.reg(R8.A), ssa.source), UnaryInsn(Loc.reg(R8.A), ssa.op, source1), CopyInsn(ssa.dest, Loc.reg(R8.A)), ) break } }) } export const convertExpr3 = (state: IRState, dest: Loc, expr: Expr): Array => { let expr_stmts: Array = [] const getSource = (expr: Expr): [Operand, Array] => { if (typeof expr === "number") { return [expr, []] } else if (typeof expr === "string") { return [Loc.vari(expr), []] } const source = state.temp() const stmts = convertExpr3(state, source, expr) return [source, stmts] } if (typeof expr === "number") { expr_stmts.push(CopyInsn(dest, expr)) } else if (typeof expr === "string") { expr_stmts.push(CopyInsn(dest, Loc.vari(expr))) } else if (expr.type === "unary") { const [source, stmts] = getSource(expr.arg) stmts.push(UnaryInsn(dest, expr.op, source)) expr_stmts = stmts } else { const [left_source, left_stmts] = getSource(expr.left) const [right_source, right_stmts] = getSource(expr.right) expr_stmts = [...left_stmts, ...right_stmts, BinaryInsn(dest, left_source, expr.op, right_source)] } return expr_stmts }