import type { AssignStmt, Expr, Stmt, UnaryOp, BinaryOp } from "./ast" type LocationType = "register" | "temporary" | "variable" type Location = { type: LocationType, name: string, } type Operand = Location | number type ASSA = { dest: Location, source: Operand } & Data export type SSACopy = ASSA<{}> // dest = source 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 type IRState = { nextID: number 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, ssa_stmts: [], } stmts.forEach(stmt => { const ssa_stmts = convertAssignStmt(state, stmt) state.ssa_stmts.push(...ssa_stmts) }) return state.ssa_stmts } export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array => { return convertExpr(state, vari(stmt.args.name), stmt.args.expr) } export const convertExpr = (state: IRState, dest: Location, expr: Expr): Array => { let expr_stmts = [] 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({ dest, op: expr.op, source, }) expr_stmts = stmts } else { const [left_source, left_stmts] = getSource(state, expr.left) const [right_source, right_stmts] = getSource(state, expr.right) const stmts = [...left_stmts, ...right_stmts] stmts.push({ dest, op: expr.op, source: left_source, source1: right_source, }) expr_stmts = stmts } return expr_stmts } 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) const stmts = convertExpr(state, source, expr) return [source, stmts] } export const prettyPrintIR = (ssa: SSA): string => { let output = "" if ("source1" in ssa) { output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)}, ${ppOp(ssa.source1)})` } else if ("op" in ssa) { output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)})` } else { 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 }