From 27c1b3308eeef62626cc573401547acdb2bb4f56 Mon Sep 17 00:00:00 2001 From: sfja Date: Wed, 26 Mar 2025 21:57:54 +0100 Subject: [PATCH] compiler: calculate new offsets --- sbc/asm_gen.ts | 144 ++++++++++++++++++++++++++++++++++++++++--------- sbc/front.ts | 1 - sbc/lir_gen.ts | 3 +- 3 files changed, 120 insertions(+), 28 deletions(-) diff --git a/sbc/asm_gen.ts b/sbc/asm_gen.ts index d7947e5..c54c77c 100644 --- a/sbc/asm_gen.ts +++ b/sbc/asm_gen.ts @@ -7,6 +7,8 @@ export class AsmGen { private liveRegs = new Set(); private regSelect = new Map(); + private layout!: StackLayout; + public constructor( private lir: lir.Program, ) {} @@ -107,28 +109,6 @@ export class AsmGen { this.writeIns(`ret`); } - private generateFnBody(fn: lir.Fn) { - this.writeln(`${fn.label}:`); - this.writeIns(`push rbp`); - this.writeIns(`mov rbp, rsp`); - this.writeIns(`sub rsp, ${fn.frameSize}`); - this.writeIns(`jmp .L${fn.mir.entry.id}`); - - for (const line of fn.lines) { - for (const label of line.labels) { - this.writeln(`.L${label}:`); - } - this.generateIns(line.ins); - } - - this.writeln(`.exit:`); - const returnLocalOffset = fn.localOffsets.get(fn.mir.returnLocal.id)!; - this.writeIns(`mov rax, QWORD ${this.relative(returnLocalOffset)}`); - this.writeIns(`mov rsp, rbp`); - this.writeIns(`pop rbp`); - this.writeIns(`ret`); - } - private generateCExporter(fn: lir.Fn, label: string) { this.writeln(`global ${label}`); this.writeln(`${label}:`); @@ -151,6 +131,57 @@ export class AsmGen { this.writeIns(`ret`); } + private generateFnBody(fn: lir.Fn) { + const allocator = new StackAllocator(); + for (const { ins } of fn.lines) { + if (ins.tag === "alloc_param") { + allocator.allocParam(ins.reg, ins.size); + } else if (ins.tag === "alloc_local") { + allocator.allocLocal(ins.reg, ins.size); + } else { + break; + } + } + this.layout = allocator.finalize(); + + this.writeln(`${fn.label}:`); + this.writeIns(`push rbp`); + this.writeIns(`mov rbp, rsp`); + + const frameSize = fn.frameSize; + const newFrameSize = this.layout.frameSize; + console.log({ frameSize, newFrameSize }); + + this.writeIns(`sub rsp, ${fn.frameSize}`); + this.writeIns(`jmp .L${fn.mir.entry.id}`); + + const isAlloc = (ins: lir.Ins): boolean => + ins.tag === "alloc_param" || ins.tag === "alloc_local"; + + for (const line of fn.lines) { + if (isAlloc(line.ins)) { + continue; + } + for (const label of line.labels) { + this.writeln(`.L${label}:`); + } + this.generateIns(line.ins); + } + + this.writeln(`.exit:`); + + const returnLocalOffset = fn.localOffsets.get(fn.mir.returnLocal.id)!; + this.writeIns(`mov rax, QWORD ${this.relative(returnLocalOffset)}`); + + const newReturnLocalOffset = this.layout + .offset(fn.localRegs.get(fn.mir.returnLocal.id)!); + console.log({ returnLocalOffset, newReturnLocalOffset }); + + this.writeIns(`mov rsp, rbp`); + this.writeIns(`pop rbp`); + this.writeIns(`ret`); + } + private generateIns(ins: lir.Ins) { const r = (reg: lir.Reg) => this.reg(reg); @@ -161,11 +192,8 @@ export class AsmGen { this.writeIns(`nop`); return; case "alloc_param": - // should already be handled - return; case "alloc_local": - // should already be handled - return; + throw new Error("should not be included"); case "mov_int": this.writeIns(`mov ${r(ins.reg)}, ${ins.val}`); return; @@ -305,3 +333,67 @@ class AsmWriter { return this.result; } } + +class StackLayout { + public constructor( + public readonly frameSize: number, + private regOffsets: Map, + ) {} + + public offset(reg: lir.Reg): number { + const offset = this.regOffsets.get(reg); + if (!offset) { + throw new Error("not found"); + } + return offset; + } +} + +class StackAllocator { + private paramRegs = new Map(); + private localRegs = new Map(); + + public allocParam(reg: lir.Reg, size: number) { + this.paramRegs.set(reg, size); + } + public allocLocal(reg: lir.Reg, size: number) { + this.localRegs.set(reg, size); + } + + public finalize(): StackLayout { + const regOffsets = new Map(); + + let currentOffset = 8; + + for (const [reg, size] of [...this.paramRegs].toReversed()) { + // Parameters are by convention 8-byte aligned. + currentOffset += align8(size); + regOffsets.set(reg, currentOffset); + } + + // Start at 8 because rbp is pushed, and + // therefore the *first value*, meaning + // the return address is at stack[top - 1]. + currentOffset = 8; + let frameSize = 0; + + // return address + currentOffset -= 8; + // caller rbp + currentOffset -= 8; + + for (const [reg, size] of this.localRegs) { + regOffsets.set(reg, currentOffset); + currentOffset -= align8(size); + frameSize += align8(size); + } + + frameSize = align(frameSize, 16); + + return new StackLayout(frameSize, regOffsets); + } +} + +const align = (value: number, alignment: number): number => + value % alignment === 0 ? value : value + (alignment - value % alignment); +const align8 = (value: number): number => align(value, 8); diff --git a/sbc/front.ts b/sbc/front.ts index c23df95..51005c0 100644 --- a/sbc/front.ts +++ b/sbc/front.ts @@ -130,7 +130,6 @@ export class Checker { const argTys = k.args .map((arg) => this.exprTy(arg)); for (const [i, param] of callee.params.entries()) { - console.log({ arg: argTys[i], param }); const tyRes = this.resolveTys(argTys[i], param); if (!tyRes.ok) { this.report( diff --git a/sbc/lir_gen.ts b/sbc/lir_gen.ts index 3b0a2c6..8d9661e 100644 --- a/sbc/lir_gen.ts +++ b/sbc/lir_gen.ts @@ -109,6 +109,7 @@ class FnGen { // return value this.localOffsets.set(this.fn.mir.returnLocal.id, currentOffset); currentOffset -= 8; + frameSize += 8; { const reg = this.reg(); @@ -116,7 +117,6 @@ class FnGen { this.localRegs.set(this.fn.mir.returnLocal.id, reg); } - frameSize += 8; for (const local of this.fn.mir.locals) { if (this.localOffsets.has(local.id)) { continue; @@ -140,6 +140,7 @@ class FnGen { this.fn.frameSize = frameSize; this.fn.localOffsets = this.localOffsets; + this.fn.localRegs = this.localRegs; for (const block of this.fn.mir.blocks) { this.currentLabels.push(this.blockLabels.get(block.id)!);