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.

108 lines
2.9 KiB

  1. import { colorGreedy, createGraph, Graph, maxCardinalitySearch } from "./data/graph"
  2. import { R8, StackOffset } from "./sm83/cpu"
  3. import { Loc } from "./ir/loc"
  4. import type { AbsInsn2 } from "./ir/insn"
  5. export type LivenessInfo = Array<Set<Loc>>
  6. export const liveness = (block: Array<AbsInsn2>): LivenessInfo => {
  7. const info: LivenessInfo = []
  8. info[block.length] = new Set()
  9. for (let i = block.length - 1; i >= 0; --i) {
  10. const insn = block[i]
  11. const last = info[i + 1]
  12. info[i] = new Set()
  13. // TODO: Add support for goto
  14. if (insn.type === "goto") {
  15. throw new Error("goto not supported in liveness analysis yet")
  16. }
  17. if ("source" in insn && typeof insn.source !== "number") {
  18. info[i].add(insn.source)
  19. }
  20. last.forEach(loc => {
  21. if (insn.dest instanceof Loc && loc.ppName() === insn.dest.ppName()) {
  22. return
  23. }
  24. info[i].add(loc)
  25. })
  26. }
  27. return info
  28. }
  29. export const locations = (block: Array<AbsInsn2>): Set<Loc> => {
  30. const ls: Set<Loc> = new Set()
  31. block.forEach(ssa => {
  32. if (ssa.type === "goto" || ssa.type === "label") {
  33. return
  34. }
  35. ls.add(ssa.dest)
  36. if (typeof ssa.source !== "number") {
  37. ls.add(ssa.source)
  38. }
  39. })
  40. return ls
  41. }
  42. export const interference = (block: Array<AbsInsn2>, live: LivenessInfo): Graph<Loc> =>
  43. createGraph((v, e) => {
  44. const locs = locations(block)
  45. locs.forEach(loc => {
  46. v(loc.ppName(), loc)
  47. })
  48. block.forEach((insn, i) =>
  49. live[i + 1].forEach(u => {
  50. if (insn.dest instanceof Loc && insn.dest.ppName() !== u.ppName()) {
  51. e(insn.dest.ppName(), u.ppName())
  52. }
  53. })
  54. )
  55. })
  56. export type RegAlloc = {
  57. [s: string]: string | StackOffset,
  58. }
  59. export const allocateRegisters = (block: Array<AbsInsn2>, registers: Array<string>): RegAlloc => {
  60. const info = liveness(block)
  61. const graph = interference(block, info)
  62. const ordering = maxCardinalitySearch(graph)
  63. const coloring = colorGreedy(graph, ordering)
  64. const allocation: RegAlloc = {}
  65. const availableRegisters = new Set(registers)
  66. const colorMap: { [c: number]: string | StackOffset } = {}
  67. let nextStackOffset = 0
  68. Object.entries(coloring.colors).forEach(([vertex, color]) => {
  69. if (typeof colorMap[color] !== 'undefined') {
  70. allocation[vertex] = colorMap[color]
  71. return
  72. }
  73. let value = null
  74. if (availableRegisters.size == 0) {
  75. value = { offset: nextStackOffset++ }
  76. } else {
  77. const result = availableRegisters.values().next()
  78. value = result.value
  79. availableRegisters.delete(value)
  80. }
  81. allocation[vertex] = value
  82. colorMap[color] = value
  83. })
  84. return allocation
  85. }