From ec84b385d36ec99f52d66df7626dbaaa6a320264 Mon Sep 17 00:00:00 2001 From: sfja Date: Fri, 17 Apr 2026 16:47:04 +0200 Subject: [PATCH] more codegen --- program.ethlang | 13 ++--- src/codegen/x86_64/isel.ts | 79 ++++++++++++++++++++++--- src/codegen/x86_64/module.ts | 9 ++- src/main.ts | 5 +- src/stringify.ts | 109 +++++++++++++++++++++++++++++++++++ 5 files changed, 195 insertions(+), 20 deletions(-) diff --git a/program.ethlang b/program.ethlang index a650c4a..6d61ffb 100644 --- a/program.ethlang +++ b/program.ethlang @@ -1,14 +1,9 @@ -fn add(a: int, b: int) -> int +fn main() -> i32 { - return a + b; -} - -fn main() -{ - let sum = add(2, 3); - print_int(sum); - print_int(2 - 3); + let a = 2; + let b = a + 5; + return b; } diff --git a/src/codegen/x86_64/isel.ts b/src/codegen/x86_64/isel.ts index 7f57613..c56ee3a 100644 --- a/src/codegen/x86_64/isel.ts +++ b/src/codegen/x86_64/isel.ts @@ -52,10 +52,10 @@ class InstructionSelector { src: Reg.reg(Regs.bp), }), ); - entry.push(new Inst({ tag: "PopR", dst: Reg.reg(Regs.bp) })); - entry.push(new Inst({ tag: "Ret" })); + exit.push(new Inst({ tag: "PopR", dst: Reg.reg(Regs.bp) })); + exit.push(new Inst({ tag: "Ret" })); - return new Fn([entry, ...blocks, exit]); + return new Fn(this.fn, [entry, ...blocks, exit]); } private generateFrameLayout() { @@ -63,14 +63,14 @@ class InstructionSelector { const align = (alignment: number) => { if (offset % alignment != 0) { - offset += alignment - offset % alignment; + offset -= alignment + offset % alignment; } }; const pushLocal = (inst: mir.Inst, size: number, alignment: number) => { align(alignment); this.locals.set(inst, offset); - offset += size; + offset -= size; }; this.fn.visit({ @@ -102,28 +102,93 @@ class InstructionSelector { }, }); + // c abi requires 16 bit stack alignment, + // might aswell do it here. align(16); - this.localsSize = offset; + this.localsSize = -offset; } private generateBasicBlock(bb: mir.BasicBlock): Block { + const regs = new Map(); + const block = new Block(); for (const inst of bb.insts) { + const save = (r: Reg): Reg => { + regs.set(inst, r); + return r; + }; + const k = inst.kind; switch (k.tag) { case "Alloca": + save(Reg.temp()); + break; + case "Void": + block.push( + new Inst({ + tag: "MovRI", + dst: save(Reg.temp()), + imm: 0, + }), + ); break; case "Int": block.push( new Inst({ tag: "MovRI", - dst: Reg.reg(Regs.bp), + dst: save(Reg.temp()), imm: k.value, }), ); break; + case "Store": + block.push( + new Inst({ + tag: "MovMRR", + dst: Reg.reg(Regs.bp), + offset: this.locals.get(k.target)!, + src: regs.get(k.source)!, + }), + ); + break; + case "Load": + block.push( + new Inst({ + tag: "MovRMR", + dst: save(Reg.temp()), + offset: this.locals.get(k.source)!, + src: Reg.reg(Regs.bp), + }), + ); + break; + case "Add": + block.push( + new Inst({ + tag: "AddR", + dst_op1: save(regs.get(k.left)!), + op2: regs.get(k.right)!, + }), + ); + break; + + case "Return": + block.push( + new Inst({ + tag: "MovRR", + dst: Reg.reg(Regs.a), + src: regs.get(k.source)!, + }), + ); + block.push( + new Inst({ + tag: "Jmp", + bb: this.exitBlock, + }), + ); + break; default: + console.error(`not implemented (${k.tag})`); // throw new Error( // `not implemented (${k.tag})`, // ); diff --git a/src/codegen/x86_64/module.ts b/src/codegen/x86_64/module.ts index 1b8787f..5737e0b 100644 --- a/src/codegen/x86_64/module.ts +++ b/src/codegen/x86_64/module.ts @@ -1,15 +1,18 @@ +import * as mir from "../../mir.ts"; + export class Module { private fns: Fn[] = []; } export class Fn { constructor( - private blocks: Block[] = [], + public mirFn: mir.Fn, + public blocks: Block[] = [], ) {} } export class Block { - private insts: Inst[] = []; + public insts: Inst[] = []; push(inst: Inst) { this.insts.push(inst); @@ -38,7 +41,7 @@ export class Reg { static tempId = 1000; private constructor( - private id: number, + public id: number, ) {} static reg(reg: Regs): Reg { diff --git a/src/main.ts b/src/main.ts index d8239c0..5ea0385 100644 --- a/src/main.ts +++ b/src/main.ts @@ -50,7 +50,10 @@ if (Deno.args.includes("--print-mir")) { } } -// const result = selectFnInstructions(mainMiddleFn); +const result = selectFnInstructions(mainMiddleFn); +stringify.printWithConsoleColors( + stringify.x86_64FnPretty(result, stringify.consoleColors), +); // console.log(JSON.stringify(result, null, 4)); if (!reporter.ok()) { diff --git a/src/stringify.ts b/src/stringify.ts index 6faad36..5879606 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -1,5 +1,6 @@ import * as ty from "./ty.ts"; import * as mir from "./mir.ts"; +import * as codegenX8664 from "./codegen/x86_64/module.ts"; export type PrettyColors = { punctuation: string; @@ -336,3 +337,111 @@ class MirFnPrettyStringifier { return id; } } + +export function x86_64FnPretty(fn: codegenX8664.Fn, colors = noColors): string { + const c = colors; + const blockIds = new Map(); + + const reg = (reg: codegenX8664.Reg): string => { + const cl = c.typeIdent; + switch (reg.id) { + case codegenX8664.Regs.a: + return `${cl}rax`; + case codegenX8664.Regs.b: + return `${cl}rbx`; + case codegenX8664.Regs.c: + return `${cl}rbc`; + case codegenX8664.Regs.si: + return `${cl}rsi`; + case codegenX8664.Regs.di: + return `${cl}rdi`; + case codegenX8664.Regs.sp: + return `${cl}rsp`; + case codegenX8664.Regs.bp: + return `${cl}rbp`; + case codegenX8664.Regs.r8: + return `${cl}r8`; + case codegenX8664.Regs.r9: + return `${cl}r9`; + case codegenX8664.Regs.r10: + return `${cl}r10`; + case codegenX8664.Regs.r11: + return `${cl}r11`; + case codegenX8664.Regs.r12: + return `${cl}r12`; + case codegenX8664.Regs.r13: + return `${cl}r13`; + case codegenX8664.Regs.r14: + return `${cl}r14`; + case codegenX8664.Regs.r15: + return `${cl}r15`; + default: + return `${c.varIdent}%${reg.id - 1000}`; + } + }; + + const imm = (v: number): string => { + return `${c.literal}${v}`; + }; + + const mem = (r: codegenX8664.Reg, off: number): string => { + return `${c.keyword}qword ${c.punctuation}[${reg(r)}${c.punctuation}${ + off !== 0 ? `-${c.literal}${-off}${c.punctuation}` : "" + }]`; + }; + + const line = (op: string, ...ops: string[]): string => { + return ` ${c.operator}${op} ${ops.join(", ")}\n`; + }; + + for (const block of fn.blocks) { + const id = blockIds.size; + blockIds.set(block, id); + } + + let result = ""; + result += `${c.fnIdent}${fn.mirFn.stmt.kind.ident}${c.punctuation}:\n`; + for (const block of fn.blocks) { + const id = blockIds.get(block)!; + result += `${c.varIdent}.l${id}${c.punctuation}:\n`; + for (const inst of block.insts) { + const k = inst.kind; + switch (k.tag) { + case "PushR": + result += line("push", reg(k.src)); + break; + case "PopR": + result += line("pop", reg(k.dst)); + break; + case "MovRR": + result += line("mov", reg(k.dst), reg(k.src)); + break; + case "SubRI": + result += line("sub", reg(k.dst), imm(k.imm)); + break; + case "MovRI": + result += line("mov", reg(k.dst), imm(k.imm)); + break; + case "MovMRR": + result += line("mov", mem(k.dst, k.offset), reg(k.src)); + break; + case "MovRMR": + result += line("mov", reg(k.dst), mem(k.src, k.offset)); + break; + case "AddR": + result += line("add", reg(k.dst_op1), reg(k.op2)); + break; + case "Jmp": + result += line( + "jmp", + `${c.varIdent}.l${blockIds.get(k.bb)!}`, + ); + break; + case "Ret": + result += line("ret"); + break; + } + } + } + return result; +}