A "high-level" language for the Gameboy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
3.2 KiB

  1. import type { AssignStmt, Expr, Stmt } from "../ast"
  2. import { R8 } from "../sm83/cpu"
  3. import { Loc, LocType } from "./loc"
  4. import type { Operand, SSA } from "./ssa"
  5. type IRState = {
  6. nextID: number
  7. ssa_stmts: Array<SSA>
  8. }
  9. export const convertIR = (stmts: Array<Stmt>): Array<SSA> => {
  10. const state: IRState = {
  11. nextID: 0,
  12. ssa_stmts: [],
  13. }
  14. stmts.forEach(stmt => {
  15. const ssa_stmts = convertAssignStmt(state, stmt)
  16. state.ssa_stmts.push(...ssa_stmts)
  17. })
  18. return state.ssa_stmts
  19. }
  20. export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array<SSA> => {
  21. return convertExpr(state, Loc.vari(stmt.args.name), stmt.args.expr)
  22. }
  23. export const convertExpr = (state: IRState, dest: Loc, expr: Expr): Array<SSA> => {
  24. const threeAddress = convertExpr3(state, dest, expr)
  25. const output: Array<SSA> = []
  26. threeAddress.forEach(ssa => {
  27. // skip copies
  28. if (!("op" in ssa)) {
  29. output.push(ssa)
  30. return
  31. } else if ("source1" in ssa) {
  32. let source1 = ssa.source1
  33. if (typeof source1 !== 'number' && source1.type === LocType.VARIABLE) {
  34. source1 = Loc.temp(`t${state.nextID++}`)
  35. output.push(
  36. { dest: Loc.reg(R8.A), source: ssa.source1 },
  37. { dest: source1, source: Loc.reg(R8.A) },
  38. )
  39. }
  40. output.push(
  41. { dest: Loc.reg(R8.A), source: ssa.source },
  42. { dest: Loc.reg(R8.A), source: Loc.reg(R8.A), op: ssa.op, source1 },
  43. { dest: ssa.dest, source: Loc.reg(R8.A) },
  44. )
  45. } else {
  46. output.push(
  47. { dest: Loc.reg(R8.A), source: ssa.source },
  48. { dest: Loc.reg(R8.A), source: Loc.reg(R8.A), op: ssa.op },
  49. { dest: ssa.dest, source: Loc.reg(R8.A) },
  50. )
  51. }
  52. })
  53. return output
  54. }
  55. export const convertExpr3 = (state: IRState, dest: Loc, expr: Expr): Array<SSA> => {
  56. let expr_stmts: Array<SSA> = []
  57. if (typeof expr === "number") {
  58. expr_stmts = [{ dest, source: expr }]
  59. } else if (typeof expr === "string") {
  60. expr_stmts = [{ dest, source: Loc.vari(expr) }]
  61. } else if (expr.type === "unary") {
  62. const [source, stmts] = getSource(state, expr.arg)
  63. stmts.push({
  64. dest,
  65. op: expr.op,
  66. source,
  67. })
  68. expr_stmts = stmts
  69. } else {
  70. const [left_source, left_stmts] = getSource(state, expr.left)
  71. const [right_source, right_stmts] = getSource(state, expr.right)
  72. const stmts = [...left_stmts, ...right_stmts]
  73. stmts.push({
  74. dest,
  75. op: expr.op,
  76. source: left_source,
  77. source1: right_source,
  78. })
  79. expr_stmts = stmts
  80. }
  81. return expr_stmts
  82. }
  83. const getSource = (state: IRState, expr: Expr): [Operand, Array<SSA>] => {
  84. if (typeof expr === "number") {
  85. return [expr, []]
  86. } else if (typeof expr === "string") {
  87. return [Loc.vari(expr), []]
  88. }
  89. const source = Loc.temp(`t${state.nextID++}`)
  90. const stmts = convertExpr3(state, source, expr)
  91. return [source, stmts]
  92. }