diff --git a/src/codegen/x86_64/isel.ts b/src/codegen/x86_64/isel.ts index 6c305ab..7f57613 100644 --- a/src/codegen/x86_64/isel.ts +++ b/src/codegen/x86_64/isel.ts @@ -1,4 +1,134 @@ import * as mir from "../../mir.ts"; +import { Block, Fn, Inst, Reg, Regs } from "./module.ts"; -export function selectFnInstructions(fn: mir.Fn) { +export function selectFnInstructions(fn: mir.Fn): Fn { + return new InstructionSelector(fn).generateFn(); +} + +class InstructionSelector { + private localsSize = 0; + private locals = new Map(); + private exitBlock!: Block; + + constructor( + private fn: mir.Fn, + ) {} + + generateFn(): Fn { + this.generateFrameLayout(); + + const entry = new Block(); + const exit = new Block(); + + entry.push(new Inst({ tag: "PushR", src: Reg.reg(Regs.bp) })); + entry.push( + new Inst({ + tag: "MovRR", + dst: Reg.reg(Regs.bp), + src: Reg.reg(Regs.sp), + }), + ); + entry.push( + new Inst({ + tag: "SubRI", + dst: Reg.reg(Regs.sp), + imm: this.localsSize, + }), + ); + + this.exitBlock = exit; + + const blocks: Block[] = []; + for (const bb of this.fn.bbs) { + blocks.push(this.generateBasicBlock(bb)); + } + + entry.push(new Inst({ tag: "Jmp", bb: blocks[0] ?? exit })); + + exit.push( + new Inst({ + tag: "MovRR", + dst: Reg.reg(Regs.sp), + src: Reg.reg(Regs.bp), + }), + ); + entry.push(new Inst({ tag: "PopR", dst: Reg.reg(Regs.bp) })); + entry.push(new Inst({ tag: "Ret" })); + + return new Fn([entry, ...blocks, exit]); + } + + private generateFrameLayout() { + let offset = 0; + + const align = (alignment: number) => { + if (offset % alignment != 0) { + offset += alignment - offset % alignment; + } + }; + + const pushLocal = (inst: mir.Inst, size: number, alignment: number) => { + align(alignment); + this.locals.set(inst, offset); + offset += size; + }; + + this.fn.visit({ + visitInst(inst) { + if (inst.kind.tag === "Alloca") { + const ty = inst.ty.as("PtrMut").kind.ty; + switch (ty.kind.tag) { + case "Int": { + switch (ty.kind.intTy) { + case "i32": + pushLocal(inst, 4, 4); + break; + case "i64": + pushLocal(inst, 8, 8); + break; + default: + throw new Error( + `not implemented (${ty.kind.intTy})`, + ); + } + break; + } + default: + throw new Error( + `not implemented (${ty.kind.tag})`, + ); + } + } + }, + }); + + align(16); + + this.localsSize = offset; + } + + private generateBasicBlock(bb: mir.BasicBlock): Block { + const block = new Block(); + for (const inst of bb.insts) { + const k = inst.kind; + switch (k.tag) { + case "Alloca": + break; + case "Int": + block.push( + new Inst({ + tag: "MovRI", + dst: Reg.reg(Regs.bp), + imm: k.value, + }), + ); + break; + default: + // throw new Error( + // `not implemented (${k.tag})`, + // ); + } + } + return block; + } } diff --git a/src/codegen/x86_64/module.ts b/src/codegen/x86_64/module.ts index 78b9947..1b8787f 100644 --- a/src/codegen/x86_64/module.ts +++ b/src/codegen/x86_64/module.ts @@ -3,7 +3,9 @@ export class Module { } export class Fn { - private blocks: Block[] = []; + constructor( + private blocks: Block[] = [], + ) {} } export class Block { @@ -15,6 +17,9 @@ export class Block { } export class Inst { + constructor( + public kind: InstKind, + ) {} } export type InstKind = @@ -26,7 +31,7 @@ export type InstKind = | { tag: "MovMRR"; dst: Reg; offset: number; src: Reg } | { tag: "MovRMR"; dst: Reg; src: Reg; offset: number } | { tag: "AddR"; dst_op1: Reg; op2: Reg } - | { tag: "Jmp"; bb: number } + | { tag: "Jmp"; bb: Block } | { tag: "Ret" }; export class Reg { diff --git a/src/front/check.ts b/src/front/check.ts index 97db688..7c8baf4 100644 --- a/src/front/check.ts +++ b/src/front/check.ts @@ -107,7 +107,7 @@ class TypeChecker { ); this.reporter.info( this.fn.kind.retTy?.loc ?? this.fn.loc, - `return type '${retTy}' defined here`, + `return type '${retTy.pretty()}' defined here`, ); this.reporter.abort(); } diff --git a/src/main.ts b/src/main.ts index c438bcb..d8239c0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import * as ast from "./ast.ts"; +import { selectFnInstructions } from "./codegen/x86_64/isel.ts"; import { Reporter } from "./diagnostics.ts"; import * as front from "./front/mod.ts"; import * as middle from "./middle.ts"; @@ -49,6 +50,9 @@ if (Deno.args.includes("--print-mir")) { } } +// const result = selectFnInstructions(mainMiddleFn); +// console.log(JSON.stringify(result, null, 4)); + if (!reporter.ok()) { reporter.printAll(); Deno.exit(1);