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.

107 lines
2.9 KiB

  1. import { colorGreedy, createGraph, Graph, maxCardinalitySearch } from "./data/graph"
  2. import { setEquals, union } from "./data/set"
  3. import { BasicBlock } from "./ir/block"
  4. import { Loc } from "./ir/loc"
  5. import { StackOffset } from "./sm83/cpu"
  6. export type LivenessInfo = Array<Set<Loc>>
  7. export const liveness = (block: BasicBlock): LivenessInfo => {
  8. const info: LivenessInfo = []
  9. info[block.insns.length] = new Set()
  10. for (let i = block.insns.length - 1; i >= 0; --i) {
  11. const insn = block.insns[i]
  12. const last = info[i + 1]
  13. info[i] = new Set()
  14. if (insn.type === "goto") {
  15. continue
  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. let dirty = true
  28. while (dirty) {
  29. dirty = false
  30. for (let i = block.insns.length - 1; i >= 0; --i) {
  31. if (block.insns[i].type !== "goto") {
  32. continue
  33. }
  34. const labelSet = info[block.labels[block.insns[i].dest as string]]
  35. if (setEquals(info[i], labelSet)) {
  36. continue
  37. }
  38. dirty = true
  39. info[i] = union(info[i], labelSet)
  40. }
  41. }
  42. return info
  43. }
  44. export const interference = (block: BasicBlock, live: LivenessInfo): Graph<Loc> =>
  45. createGraph((v, e) => {
  46. block.locs().forEach(loc => {
  47. v(loc.ppName(), loc)
  48. })
  49. block.insns.forEach((insn, i) =>
  50. live[i + 1].forEach(u => {
  51. if (insn.dest instanceof Loc && insn.dest.ppName() !== u.ppName()) {
  52. e(insn.dest.ppName(), u.ppName())
  53. }
  54. })
  55. )
  56. })
  57. export type RegAlloc = {
  58. [s: string]: string | StackOffset,
  59. }
  60. export const allocateRegisters = (block: BasicBlock, registers: Array<string>): RegAlloc => {
  61. const info = liveness(block)
  62. const graph = interference(block, info)
  63. const ordering = maxCardinalitySearch(graph)
  64. const coloring = colorGreedy(graph, ordering)
  65. const allocation: RegAlloc = {}
  66. const availableRegisters = new Set(registers)
  67. const colorMap: { [c: number]: string | StackOffset } = {}
  68. let nextStackOffset = 0
  69. Object.entries(coloring.colors).forEach(([vertex, color]) => {
  70. if (typeof colorMap[color] !== 'undefined') {
  71. allocation[vertex] = colorMap[color]
  72. return
  73. }
  74. let value = null
  75. if (availableRegisters.size == 0) {
  76. value = { offset: nextStackOffset++ }
  77. } else {
  78. const result = availableRegisters.values().next()
  79. value = result.value
  80. availableRegisters.delete(value)
  81. }
  82. allocation[vertex] = value
  83. colorMap[color] = value
  84. })
  85. return allocation
  86. }