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.

120 lines
3.2 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[getLabelIndex(block, 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 getLabelIndex = (block: BasicBlock, label: string): number => {
  45. let idx = -1
  46. for (let i = 0; i < block.insns.length; ++i) {
  47. if (block.insns[i].type === "label" && block.insns[i].dest === label) {
  48. idx = i
  49. break
  50. }
  51. }
  52. return idx
  53. }
  54. export const interference = (block: BasicBlock, live: LivenessInfo): Graph<Loc> =>
  55. createGraph((v, e) => {
  56. block.locs().forEach(loc => {
  57. v(loc.ppName(), loc)
  58. })
  59. block.insns.forEach((insn, i) =>
  60. live[i + 1].forEach(u => {
  61. if (insn.dest instanceof Loc && insn.dest.ppName() !== u.ppName()) {
  62. e(insn.dest.ppName(), u.ppName())
  63. }
  64. })
  65. )
  66. })
  67. export type RegAlloc = {
  68. [s: string]: string | StackOffset,
  69. }
  70. export const allocateRegisters = (block: BasicBlock, registers: Array<string>): RegAlloc => {
  71. const info = liveness(block)
  72. const graph = interference(block, info)
  73. const ordering = maxCardinalitySearch(graph)
  74. const coloring = colorGreedy(graph, ordering)
  75. const allocation: RegAlloc = {}
  76. const availableRegisters = new Set(registers)
  77. const colorMap: { [c: number]: string | StackOffset } = {}
  78. let nextStackOffset = 0
  79. Object.entries(coloring.colors).forEach(([vertex, color]) => {
  80. if (typeof colorMap[color] !== 'undefined') {
  81. allocation[vertex] = colorMap[color]
  82. return
  83. }
  84. let value = null
  85. if (availableRegisters.size == 0) {
  86. value = { offset: nextStackOffset++ }
  87. } else {
  88. const result = availableRegisters.values().next()
  89. value = result.value
  90. availableRegisters.delete(value)
  91. }
  92. allocation[vertex] = value
  93. colorMap[color] = value
  94. })
  95. return allocation
  96. }