Browse Source

Finish code generation for copies & unary ops

master
Forest Belton 3 years ago
parent
commit
9354d16e57
4 changed files with 136 additions and 32 deletions
  1. +106
    -24
      lib/asm.ts
  2. +17
    -3
      lib/compile.ts
  3. +7
    -3
      lib/ir.ts
  4. +6
    -2
      lib/main.ts

+ 106
- 24
lib/asm.ts View File

@ -1,4 +1,4 @@
import type { SSA } from "./ir"
import type { SSA, SSABinary, SSACopy, SSAUnary } from "./ir"
const reg8 = ["B", "C", "D", "E"] as const
@ -62,42 +62,124 @@ export const convertASM = (ir: Array): Array => {
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 => {
const dest = state.alloc_sym_loc(ssa.dest)
if ("source1" in ssa) { // Binary
convertASM_SSA_Binary(state, ssa)
} else if ("op" in ssa) { // Unary
convertASM_SSA_Unary(state, ssa)
} else { // Copy
let source = ""
if (typeof ssa.source == "number") {
source = ssa.source.toString()
convertASM_SSA_Copy(state, ssa)
}
}
export const convertASM_SSA_Binary = (state: ASMState, ssa: SSABinary): void => {
const dest = state.alloc_sym_loc(ssa.dest)
}
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 {
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"
}
state.insns.push(
`LD HL, SP + (${loc})`,
`LD A, (HL)`
)
}
}
if (typeof dest === "string") {
state.insns.push(`LD ${dest}, ${source}`)
switch (ssa.op) {
case '-':
state.insns.push(
`CPL`,
`INC A`,
)
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 + (${dest})`,
`LD (HL), ${source}`
`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}`
)
}
}

+ 17
- 3
lib/compile.ts View File

@ -15,7 +15,7 @@ type SymbolMap = {
}
// TODO: Support more than one TU
export const compile = (source: string): string => {
export const compile = (fileName: string, source: string): string => {
// 1. Parse
const ast = parse(source)
@ -26,6 +26,9 @@ export const compile = (source: string): string => {
// 3. Create top-level symbol map
const symbols = processDecls(decls)
// TODO: Support declaring types other than U8/S8
const asmDecls = Object.values(symbols).map(symbol => `${symbol.name}:: DB`)
// TODO: Some form of type-checking
// 4. Generate IR
@ -36,10 +39,21 @@ export const compile = (source: string): string => {
// 5. Generate code
const insns = convertASM(ir)
const code = insns.join("\n")
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 code
return output
}
export const processDecls = (decls: Array<Decl>): SymbolMap => decls.reduce(processDecl, {})

+ 7
- 3
lib/ir.ts View File

@ -5,9 +5,13 @@ type Operand = string | number
type ASSA<Data> = { dest: string, source: Operand } & Data
export type SSA = ASSA<{ source: Operand }> // dest = source
| ASSA<{ op: string }> // dest = op(source)
| ASSA<{ op: string, source1: Operand }> // dest = op(source, source1)
export type SSACopy = ASSA<{ source: Operand }> // dest = source
export type SSAUnary = ASSA<{ op: string }> // dest = op(source)
export type SSABinary = ASSA<{ op: string, source1: Operand }> // dest = op(source, source1)
export type SSA = SSACopy | SSAUnary | SSABinary
type IRState = {
nextID: number

+ 6
- 2
lib/main.ts View File

@ -1,15 +1,19 @@
import { promises } from "fs"
import { basename, resolve } from "path"
import { compile } from "./compile"
const { readFile } = promises
if (process.argv.length !== 3) {
console.error("usage: gbuoy <program.b>")
console.error("usage: gbuoy <program.gby>")
process.exit(1)
}
const fileName = process.argv[2]
const localName = basename(resolve(fileName))
readFile(fileName, { encoding: "utf-8" }).then(source => {
const output = compile(source)
const output = compile(localName, source)
console.log(output)
})

Loading…
Cancel
Save