Browse Source

Improve IR format

master
Forest Belton 2 years ago
parent
commit
f2d54fde09
5 changed files with 93 additions and 275 deletions
  1. +0
    -240
      lib/asm.ts
  2. +6
    -2
      lib/ast.d.ts
  3. +20
    -17
      lib/compile.ts
  4. +1
    -1
      lib/graph/graph.ts
  5. +66
    -15
      lib/ir.ts

+ 0
- 240
lib/asm.ts View File

@ -1,240 +0,0 @@
import type { SSA, SSABinary, SSACopy, SSAUnary } from "./ir"
const reg8 = ["B", "C", "D", "E"] as const
type R8 = typeof reg8[number]
class ASMState {
regs: { [r in R8]: string | null }
syms: { [s: string]: R8 }
stack_offsets: { [s: string]: number }
cur_offset: number
insns: Array<string>
constructor() {
this.regs = { "B": null, "C": null, "D": null, "E": null }
this.syms = {}
this.stack_offsets = {}
this.cur_offset = 0
this.insns = []
}
// TODO: Generalize for u16
get_sym_loc(sym: string): R8 | number | null {
// return register if already allocated
if (typeof this.syms[sym] !== "undefined") {
return this.syms[sym]
}
// return stack offset if already allocated
if (typeof this.stack_offsets[sym] !== "undefined") {
return this.cur_offset - this.stack_offsets[sym]
}
return null
}
alloc_sym_loc(sym: string): R8 | number {
const existing_loc = this.get_sym_loc(sym)
if (existing_loc !== null) {
return existing_loc
}
// look for free register
for (const r8 of reg8) {
if (this.regs[r8] === null) {
this.regs[r8] = sym
this.syms[sym] = r8
return r8
}
}
// allocate space on stack
this.cur_offset++
this.stack_offsets[sym] = this.cur_offset
this.insns.push("ADD SP, -1")
return this.stack_offsets[sym]
}
}
export const convertASM = (ir: Array<SSA>): Array<string> => {
const state = new ASMState()
ir.forEach(ssa => convertASM_SSA(state, ssa))
for (let sym of Object.keys(state.syms)) {
if (sym.startsWith("temp")) {
continue
}
state.insns.push(
`LD A, ${state.syms[sym]}`,
`LD (${sym}), A`
)
}
for (let sym of Object.keys(state.stack_offsets)) {
if (sym.startsWith("temp")) {
continue
}
state.insns.push(
`LD HL, SP + (${state.cur_offset - state.stack_offsets[sym]})`,
`LD A, (HL)`,
`LD (${sym}), A`
)
}
if (state.cur_offset !== 0) {
state.insns.push(`ADD SP, ${state.cur_offset}`)
}
return state.insns
}
// TODO: Track liveness
const convertASM_SSA = (state: ASMState, ssa: SSA): void => {
if ("source1" in ssa) { // Binary
convertASM_SSA_Binary(state, ssa)
} else if ("op" in ssa) { // Unary
convertASM_SSA_Unary(state, ssa)
} else { // Copy
convertASM_SSA_Copy(state, ssa)
}
}
export const convertASM_SSA_Binary = (state: ASMState, ssa: SSABinary): void => {
const dest = state.alloc_sym_loc(ssa.dest)
if (typeof ssa.source === "number") {
state.insns.push(`LD A, ${ssa.source}`)
} else {
const loc = state.get_sym_loc(ssa.source)
if (loc === null) {
state.insns.push(`LD A, (${ssa.source})`)
} else if (typeof loc === "string") {
state.insns.push(`LD A, ${loc}`)
} else {
state.insns.push(
`LD HL, SP + (${loc})`,
`LD A, (HL)`
)
}
}
switch (ssa.op) {
case "add":
if (typeof ssa.source1 === "number") {
state.insns.push(`ADD ${ssa.source1}`)
} else {
const loc = state.get_sym_loc(ssa.source1)
if (loc === null) {
throw new Error('fuck')
} else if (typeof loc === "string") {
state.insns.push(`ADD ${loc}`)
} else {
throw new Error('fuck')
state.insns.push(
`LD HL, SP + (${loc})`
)
}
}
break
default:
throw new Error(`unsupported binary op \`${ssa.op}'`)
}
if (typeof dest === "string") {
state.insns.push(`LD ${dest}, A`)
} else {
state.insns.push(
`LD HL, SP + (${dest})`,
`LD (HL), A`
)
}
}
export const convertASM_SSA_Unary = (state: ASMState, ssa: SSAUnary): void => {
const dest = state.alloc_sym_loc(ssa.dest)
if (typeof ssa.source == "number") {
state.insns.push(`LD A, ${ssa.source}`)
} else {
const loc = state.get_sym_loc(ssa.source)
if (loc === null) {
state.insns.push(`LD A, (${ssa.source})`)
} else if (typeof loc === "string") {
state.insns.push(`LD A, ${loc}`)
} else {
state.insns.push(
`LD HL, SP + (${loc})`,
`LD A, (HL)`
)
}
}
switch (ssa.op) {
case 'arith_negate':
state.insns.push(
`CPL`,
`INC A`,
)
break
case 'bit_negate':
state.insns.push(
`CPL`
)
break
default:
throw new Error(`unsupported unary op \`'${ssa.op}`)
}
if (typeof dest === "string") {
state.insns.push(`LD ${dest}, A`)
} else {
state.insns.push(
`LD HL, SP + (${dest})`,
`LD (HL), A`
)
}
}
export const convertASM_SSA_Copy = (state: ASMState, ssa: SSACopy): void => {
const dest = state.alloc_sym_loc(ssa.dest)
let source = ""
if (typeof ssa.source == "number") {
source = ssa.source.toString()
} else {
const loc = state.get_sym_loc(ssa.source)
if (loc === null) {
state.insns.push(`LD A, (${ssa.source})`)
source = "A"
} else if (typeof loc === "string") {
source = loc
} else {
state.insns.push(
`LD HL, SP + (${loc})`,
`LD A, (HL)`
)
source = "A"
}
}
if (typeof dest === "string") {
state.insns.push(`LD ${dest}, ${source}`)
} else {
state.insns.push(
`LD HL, SP + (${dest})`,
`LD (HL), ${source}`
)
}
}

+ 6
- 2
lib/ast.d.ts View File

@ -34,19 +34,23 @@ export type Expr = BinaryExpr | UnaryExpr | BasicExpr
type BinaryExpr = {
type: "binary",
op: string,
op: BinaryOp,
left: Expr,
right: Expr,
}
type UnaryExpr = {
type: "unary",
op: string,
op: UnaryOp,
arg: Expr,
}
type BasicExpr = number | string
type UnaryOp = "arith_negate" | "bit_negate"
type BinaryOp = "add" | "subtract" | "shift_left" | "shift_right" | "bit_and"
| "bit_xor" | "bit_or"
// Attributes
export type Attr = {
name: string,

+ 20
- 17
lib/compile.ts View File

@ -1,6 +1,7 @@
import { convertASM } from "./asm"
// import { convertASM } from "./asm"
import { convertIR, prettyPrintIR } from "./ir"
import { parse } from "./parser"
import { inspect } from "util"
import type { Attr, Decl, Stmt, Type, VarDecl } from "./ast"
@ -38,22 +39,24 @@ export const compile = (fileName: string, source: string): string => {
console.log(ir.map(prettyPrintIR).join("\n"))
// 5. Generate code
const insns = convertASM(ir)
let output = ''
if (asmDecls.length > 0) {
output += `SECTION "${fileName} Data", WRAM0\n\n`
output += asmDecls.join("\n")
output += "\n\n"
}
if (insns.length > 0) {
output += `SECTION "${fileName} Code", ROM0\n\n`
output += insns.join("\n")
}
console.log("=== ASM === ")
return output
/* const insns = convertASM(ir)
let output = ''
if (asmDecls.length > 0) {
output += `SECTION "${fileName} Data", WRAM0\n\n`
output += asmDecls.join("\n")
output += "\n\n"
}
if (insns.length > 0) {
output += `SECTION "${fileName} Code", ROM0\n\n`
output += insns.join("\n")
}
console.log("=== ASM === ")
return output */
return ''
}
export const processDecls = (decls: Array<Decl>): SymbolMap => decls.reduce(processDecl, {})

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

@ -48,7 +48,7 @@ export const maxCardinalitySearch = (g: Graph): Array => {
const weights: { [s: string]: number } = {}
const ordering: Array<string> = []
let W = vertices(g)
const W = vertices(g)
const numVertices = W.size
for (let i = 0; i < numVertices; ++i) {

+ 66
- 15
lib/ir.ts View File

@ -1,14 +1,21 @@
import type { AssignStmt, Expr, Stmt } from "./ast"
import type { AssignStmt, Expr, Stmt, UnaryOp, BinaryOp } from "./ast"
type Operand = string | number
type LocationType = "register" | "temporary" | "variable"
type ASSA<Data> = { dest: string, source: Operand } & Data
type Location = {
type: LocationType,
name: string,
}
type Operand = Location | number
export type SSACopy = ASSA<{ source: Operand }> // dest = source
type ASSA<Data> = { dest: Location, source: Operand } & Data
export type SSAUnary = ASSA<{ op: string }> // dest = op(source)
export type SSACopy = ASSA<{}> // dest = source
export type SSABinary = ASSA<{ op: string, source1: Operand }> // dest = op(source, source1)
export type SSAUnary = ASSA<{ op: UnaryOp }> // dest = op(source)
export type SSABinary = ASSA<{ op: BinaryOp, source1: Operand }> // dest = op(source, source1)
export type SSA = SSACopy | SSAUnary | SSABinary
@ -17,6 +24,21 @@ type IRState = {
ssa_stmts: Array<SSA>
}
const temp = (state: IRState): Location => ({
type: "temporary",
name: `t${state.nextID++}`,
})
const vari = (name: string): Location => ({
type: "variable",
name,
})
const reg = (name: string): Location => ({
type: "register",
name,
})
export const convertIR = (stmts: Array<Stmt>): Array<SSA> => {
const state: IRState = {
nextID: 0,
@ -32,14 +54,16 @@ export const convertIR = (stmts: Array): Array => {
}
export const convertAssignStmt = (state: IRState, stmt: AssignStmt): Array<SSA> => {
return convertExpr(state, stmt.args.name, stmt.args.expr)
return convertExpr(state, vari(stmt.args.name), stmt.args.expr)
}
export const convertExpr = (state: IRState, dest: string, expr: Expr): Array<SSA> => {
export const convertExpr = (state: IRState, dest: Location, expr: Expr): Array<SSA> => {
let expr_stmts = []
if (typeof expr === "number" || typeof expr === "string") {
if (typeof expr === "number") {
expr_stmts = [{ dest, source: expr }]
} else if (typeof expr === "string") {
expr_stmts = [{ dest, source: vari(expr) }]
} else if (expr.type === "unary") {
const [source, stmts] = getSource(state, expr.arg)
stmts.push({
@ -67,12 +91,14 @@ export const convertExpr = (state: IRState, dest: string, expr: Expr): Array
return expr_stmts
}
const getSource = (state: IRState, expr: Expr): [string | number, Array<SSA>] => {
if (typeof expr === "number" || typeof expr === "string") {
const getSource = (state: IRState, expr: Expr): [Operand, Array<SSA>] => {
if (typeof expr === "number") {
return [expr, []]
} else if (typeof expr === "string") {
return [vari(expr), []]
}
const source = `temp${state.nextID++}`
const source = temp(state)
const stmts = convertExpr(state, source, expr)
return [source, stmts]
}
@ -81,11 +107,36 @@ export const prettyPrintIR = (ssa: SSA): string => {
let output = ""
if ("source1" in ssa) {
output = `${ssa.dest} = ${ssa.op}(${ssa.source}, ${ssa.source1})`
output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)}, ${ppOp(ssa.source1)})`
} else if ("op" in ssa) {
output = `${ssa.dest} = ${ssa.op}(${ssa.source})`
output = `${ppLoc(ssa.dest)} = ${ssa.op}(${ppOp(ssa.source)})`
} else {
output = `${ssa.dest} = ${ssa.source}`
output = `${ppLoc(ssa.dest)} = ${ppOp(ssa.source)}`
}
return output
}
const ppOp = (op: Operand): string =>
typeof op === "number"
? op.toString()
: ppLoc(op)
const ppLoc = (loc: Location): string => {
let output = ''
switch (loc.type) {
case "register":
output = `%${loc.name}`
break
case "temporary":
output = `#${loc.name}`
break
case "variable":
output = loc.name
break
}
return output

Loading…
Cancel
Save