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.

134 lines
3.8 KiB

  1. import { R8 } from "../sm83/cpu"
  2. import { Loc, LocType } from "../ir/loc"
  3. import type { Operand, SSA, SSABinary, SSACopy, SSAUnary } from "../ir/ssa"
  4. import type { RegAlloc } from "../regalloc"
  5. import { UnaryOp } from "../ast"
  6. export const generateBlock = (alloc: RegAlloc, block: Array<SSA>): Array<string> => {
  7. const output: Array<string> = []
  8. block.forEach(ssa => {
  9. output.push(...generateSSA(alloc, ssa))
  10. })
  11. return output
  12. }
  13. export const generateSSA = (alloc: RegAlloc, ssa: SSA): Array<string> => {
  14. let output: Array<string> = []
  15. if ("source1" in ssa) {
  16. output = generateSSA_Binary(alloc, ssa)
  17. } else if ("op" in ssa) {
  18. output = generateSSA_Unary(alloc, ssa)
  19. } else {
  20. output = generateSSA_Copy(alloc, ssa)
  21. }
  22. return output
  23. }
  24. export const generateSSA_Copy = (alloc: RegAlloc, ssa: SSACopy): Array<string> => {
  25. const output: Array<string> = []
  26. const source = typeof ssa.source === 'number'
  27. ? ssa.source.toString()
  28. : getAlloc(alloc, ssa.source).name
  29. switch (ssa.dest.type) {
  30. case LocType.TEMPORARY:
  31. const dest = getAlloc(alloc, ssa.dest)
  32. // TODO: Remove later with peephole optimizer
  33. if (dest.name !== source) {
  34. output.push(`LD ${dest.name}, ${source}`)
  35. }
  36. break
  37. case LocType.REGISTER:
  38. if (isA(ssa.dest) && typeof ssa.source !== 'number' && ssa.source.type === LocType.VARIABLE) {
  39. output.push(`LD A, (${ssa.source.name})`)
  40. // TODO: Remove check with peephole optimizer
  41. } else if (source !== ssa.dest.name) {
  42. output.push(`LD ${ssa.dest.name}, ${source}`)
  43. }
  44. break
  45. case LocType.VARIABLE:
  46. if (source !== R8.A) {
  47. console.log(source, ssa)
  48. throw new Error("unsupported")
  49. }
  50. output.push(`LD (${ssa.dest.name}), A`)
  51. break
  52. default:
  53. throw new Error(`unsupported destination type \`${ssa.dest.type}'`)
  54. }
  55. return output
  56. }
  57. const getAlloc = (alloc: RegAlloc, loc: Loc): Loc => {
  58. const destAlloc = alloc[loc.toString()]
  59. if (typeof destAlloc === "object") {
  60. throw new Error("stack variables not yet supported")
  61. }
  62. return Loc.reg(destAlloc)
  63. }
  64. export const generateSSA_Unary = (alloc: RegAlloc, ssa: SSAUnary): Array<string> => {
  65. if (!isA(ssa.dest)) {
  66. throw new Error("Unexpected form for unary operation")
  67. }
  68. const output: Array<string> = []
  69. if (typeof ssa.source === "number") {
  70. output.push(`LD A, ${ssa.source}`)
  71. } else if (ssa.source.type === LocType.REGISTER) {
  72. if (!isA(ssa.dest)) {
  73. output.push(`LD A, ${ssa.source.name}`)
  74. }
  75. } else {
  76. // TODO: ??
  77. output.push(`LD A, ${alloc[ssa.source.toString()]}`)
  78. }
  79. const ops = unaryOps[ssa.op]
  80. if (!ops) {
  81. throw new Error(`unsupported unary op \`${ssa.op}'`)
  82. }
  83. return output.concat(ops)
  84. }
  85. const unaryOps = {
  86. "arith_negate": ["CPL", "INC A"],
  87. "bit_negate": ["CPL"]
  88. }
  89. export const generateSSA_Binary = (alloc: RegAlloc, ssa: SSABinary): Array<string> => {
  90. if (!isA(ssa.dest) || !isA(ssa.source)) {
  91. throw new Error("Unexpected form for binary operation")
  92. }
  93. const output: Array<string> = []
  94. const source = typeof ssa.source1 === 'number'
  95. ? ssa.source1
  96. : getAlloc(alloc, ssa.source1).name
  97. switch (ssa.op) {
  98. case "add":
  99. output.push(`ADD ${source}`)
  100. break
  101. default:
  102. throw new Error(`unsupported binary op \`${ssa.op}'`)
  103. }
  104. return output
  105. }
  106. const isA = (loc: Operand): boolean =>
  107. typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A