Browse Source

Finish basic register allocation

master
Forest Belton 3 years ago
parent
commit
1a944de1a1
6 changed files with 81 additions and 42 deletions
  1. +1
    -1
      lib/data/graph.ts
  2. +1
    -1
      lib/data/set.ts
  3. +0
    -33
      lib/live.ts
  4. +50
    -6
      lib/regalloc.ts
  5. +12
    -0
      lib/sm83/cpu.ts
  6. +17
    -1
      test/regalloc.spec.ts

+ 1
- 1
lib/data/graph.ts View File

@ -111,7 +111,7 @@ export const colorGreedy = (g: Graph, vertexOrdering: Array): Grap
} }
const coloring: GraphColoring = { numColors: 0, colors: {} } const coloring: GraphColoring = { numColors: 0, colors: {} }
const usedColors = {}
const usedColors: { [c: number]: boolean } = {}
vertexOrdering.forEach(v => { vertexOrdering.forEach(v => {
const ns = neighbors(g, v) const ns = neighbors(g, v)

+ 1
- 1
lib/data/set.ts View File

@ -15,7 +15,7 @@ export const setEquals = (xs: Set, ys: Set): boolean => {
return false return false
} }
for (const x of xs) {
for (const x of Array.from(xs)) {
if (!ys.has(x)) { if (!ys.has(x)) {
return false return false
} }

+ 0
- 33
lib/live.ts View File

@ -1,33 +0,0 @@
import type { Loc } from "./ir/loc"
import type { SSA } from "./ir/ssa"
export type LivenessInfo = Array<Set<Loc>>
export const liveness = (block: Array<SSA>): LivenessInfo => {
const info: LivenessInfo = []
for (let i = block.length - 1; i >= 0; --i) {
const insn = block[i]
const last = info[i + 1] || new Set()
info[i] = new Set()
if (typeof insn.source !== "number") {
info[i].add(insn.source)
}
if ("source1" in insn && typeof insn.source1 !== "number") {
info[i].add(insn.source1)
}
last.forEach(loc => {
if (loc === insn.dest) {
return
}
info[i].add(loc)
})
}
return info
}

+ 50
- 6
lib/regalloc.ts View File

@ -1,5 +1,5 @@
import { createGraph, Graph } from "./data/graph"
import { colorGreedy, createGraph, Graph, maxCardinalitySearch } from "./data/graph"
import { R8, StackOffset } from "./sm83/cpu"
import type { Loc } from "./ir/loc" import type { Loc } from "./ir/loc"
import type { SSA } from "./ir/ssa" import type { SSA } from "./ir/ssa"
@ -39,10 +39,14 @@ export const locations = (block: Array): Set => {
const ls: Set<Loc> = new Set() const ls: Set<Loc> = new Set()
block.forEach(ssa => { block.forEach(ssa => {
ls.add(ssa.dest)
if (typeof ssa.source !== "number") {
ls.add(ssa.source)
}
if ("source1" in ssa && typeof ssa.source1 !== "number") { if ("source1" in ssa && typeof ssa.source1 !== "number") {
ls.add(ssa.source1) ls.add(ssa.source1)
} else if (typeof ssa.source !== "number") {
ls.add(ssa.source)
} }
}) })
@ -51,13 +55,53 @@ export const locations = (block: Array): Set => {
export const interference = (block: Array<SSA>, live: LivenessInfo): Graph<Loc> => export const interference = (block: Array<SSA>, live: LivenessInfo): Graph<Loc> =>
createGraph((v, e) => { createGraph((v, e) => {
const locs = locations(block)
locs.forEach(loc => {
v(loc.toString(), loc)
})
block.forEach((ssa, i) => block.forEach((ssa, i) =>
live[i + 1].forEach(u => { live[i + 1].forEach(u => {
if (ssa.dest.toString() !== u.toString()) { if (ssa.dest.toString() !== u.toString()) {
v(ssa.dest.toString(), ssa.dest)
v(u.toString(), u)
e(ssa.dest.toString(), u.toString()) e(ssa.dest.toString(), u.toString())
} }
}) })
) )
}) })
type RegAlloc = {
[s: string]: R8 | StackOffset,
}
export const allocateRegisters = (block: Array<SSA>): RegAlloc => {
const info = liveness(block)
const graph = interference(block, info)
const ordering = maxCardinalitySearch(graph)
const coloring = colorGreedy(graph, ordering)
const allocation: RegAlloc = {}
const availableRegisters = new Set(Object.values(R8))
const colorMap: { [c: number]: R8 | StackOffset } = {}
let nextStackOffset = 0
Object.entries(coloring.colors).forEach(([vertex, color]) => {
if (typeof colorMap[color] !== 'undefined') {
allocation[vertex] = colorMap[color]
return
}
let value = null
if (availableRegisters.size == 0) {
value = { offset: nextStackOffset++ }
} else {
const result = availableRegisters.values().next()
value = result.value
availableRegisters.delete(value)
}
allocation[vertex] = value
colorMap[color] = value
})
return allocation
}

+ 12
- 0
lib/sm83/cpu.ts View File

@ -0,0 +1,12 @@
export enum R8 {
A = "A",
B = "B",
C = "C",
D = "D",
E = "E",
H = "H",
L = "L",
HL = "(HL)",
}
export type StackOffset = { offset: number }

+ 17
- 1
test/regalloc.spec.ts View File

@ -3,7 +3,7 @@ import { edgeConnects } from "../lib/data/graph"
import { Loc } from "../lib/ir/loc" import { Loc } from "../lib/ir/loc"
import type { SSA } from "../lib/ir/ssa" import type { SSA } from "../lib/ir/ssa"
import { interference, liveness } from "../lib/regalloc"
import { allocateRegisters, interference, liveness } from "../lib/regalloc"
describe("liveness", () => { describe("liveness", () => {
it("computes liveness", () => { it("computes liveness", () => {
@ -42,3 +42,19 @@ describe("interference", () => {
expect(edgeConnects(g, "a", "b")).to.be.true expect(edgeConnects(g, "a", "b")).to.be.true
}) })
}) })
describe("allocateRegisters", () => {
it("allocates registers", () => {
const block: Array<SSA> = [
{ dest: Loc.vari("a"), source: 7 },
{ dest: Loc.vari("b"), source: 3 },
{ dest: Loc.vari("x"), source: Loc.vari("a") }
]
const alloc = allocateRegisters(block)
expect(alloc.a).to.equal("A")
expect(alloc.b).to.equal("B")
expect(alloc.x).to.equal("A")
})
})

Loading…
Cancel
Save