compiler: move layout to asm_gen

This commit is contained in:
sfja 2025-03-26 22:15:39 +01:00
parent 27c1b3308e
commit be04fa1b6e
5 changed files with 82 additions and 70 deletions

View File

@ -144,24 +144,17 @@ export class AsmGen {
}
this.layout = allocator.finalize();
const returnLocalOffset = this.layout
.offset(fn.localRegs.get(fn.mir.returnLocal.id)!);
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(`sub rsp, ${this.layout.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}:`);
}
@ -169,14 +162,7 @@ export class AsmGen {
}
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`);
@ -193,7 +179,8 @@ export class AsmGen {
return;
case "alloc_param":
case "alloc_local":
throw new Error("should not be included");
// Handled elsewhere.
return;
case "mov_int":
this.writeIns(`mov ${r(ins.reg)}, ${ins.val}`);
return;
@ -211,17 +198,23 @@ export class AsmGen {
return;
case "load":
this.writeIns(
`mov ${r(ins.reg)}, QWORD ${this.relative(ins.offset)}`,
`mov ${r(ins.reg)}, QWORD ${
this.relative(this.layout.offset(ins.sReg))
}`,
);
return;
case "store_reg":
this.writeIns(
`mov QWORD ${this.relative(ins.offset)}, ${r(ins.reg)}`,
`mov QWORD ${
this.relative(this.layout.offset(ins.sReg))
}, ${r(ins.reg)}`,
);
return;
case "store_imm":
this.writeIns(
`mov QWORD ${this.relative(ins.offset)}, ${ins.val}`,
`mov QWORD ${
this.relative(this.layout.offset(ins.sReg))
}, ${ins.val}`,
);
return;
case "call_reg":
@ -388,7 +381,8 @@ class StackAllocator {
frameSize += align8(size);
}
frameSize = align(frameSize, 16);
// frameSize - 8 is safe because frameSize is always >= 8.
frameSize = align(frameSize - 8, 16);
return new StackLayout(frameSize, regOffsets);
}

50
sbc/attr.ts Normal file
View File

@ -0,0 +1,50 @@
import * as ast from "./ast.ts";
export class AttrView {
public constructor(
private attrs: ast.Attr[],
) {}
public static fromStmt(stmt: ast.Stmt): AttrView {
if (stmt.kind.tag !== "fn") {
throw new Error("invalid statement");
}
return new AttrView(stmt.kind.attrs);
}
public has(ident: string): boolean {
return this.attrs
.some((attr) => attr.ident === ident);
}
public get(ident: string): OneAttrView {
const attr = this.attrs
.find((attr) => attr.ident === ident);
if (!attr) {
throw new Error("not found");
}
return new OneAttrView(attr);
}
}
export class OneAttrView {
public constructor(
private attr: ast.Attr,
) {}
public get args(): number {
return this.attr.args.length;
}
public isStr(argIdx: number): boolean {
return this.attr.args[argIdx].kind.tag === "str";
}
public strVal(argIdx: number): string {
const arg = this.attr.args[argIdx];
if (arg.kind.tag !== "str") {
throw new Error("invalid argument expression");
}
return arg.kind.val;
}
}

View File

@ -10,8 +10,6 @@ export type Fn = {
label: string;
mir: mir.Fn;
lines: Line[];
frameSize: number;
localOffsets: Map<number, number>;
localRegs: Map<number, Reg>;
};
@ -31,9 +29,9 @@ export type Ins =
| { tag: "mov_fn"; reg: Reg; fn: Fn }
| { tag: "push"; reg: Reg }
| { tag: "pop"; reg: Reg }
| { tag: "load"; reg: Reg; offset: number }
| { tag: "store_reg"; offset: number; reg: Reg }
| { tag: "store_imm"; offset: number; val: number }
| { tag: "load"; reg: Reg; sReg: Reg }
| { tag: "store_reg"; sReg: Reg; reg: Reg }
| { tag: "store_imm"; sReg: Reg; val: number }
| { tag: "call_reg"; reg: Reg; args: number }
| { tag: "call_imm"; fn: Fn; args: number }
| { tag: "jmp"; target: Label }
@ -89,11 +87,11 @@ export class ProgramStringifyer {
case "pop":
return `pop %${ins.reg}`;
case "load":
return `load %${ins.reg}, [${ins.offset}]`;
return `load %${ins.reg}, [%${ins.sReg}]`;
case "store_reg":
return `store_reg [${ins.offset}], %${ins.reg}`;
return `store_reg [%${ins.sReg}], %${ins.reg}`;
case "store_imm":
return `store_val [${ins.offset}], ${ins.val}`;
return `store_val [%${ins.sReg}], ${ins.val}`;
case "call_reg":
return `call_reg %${ins.reg}, ${ins.args}`;
case "call_imm":

View File

@ -36,8 +36,6 @@ export class LirGen {
label,
mir,
lines: [],
frameSize: 0,
localOffsets: new Map(),
localRegs: new Map(),
};
this.fns.set(id, fn);
@ -77,7 +75,6 @@ class FnGen {
private currentLabels: Label[] = [];
private localOffsets = new Map<number, number>();
private localRegs = new Map<number, Reg>();
public constructor(
@ -92,39 +89,16 @@ class FnGen {
this.blockLabels.set(block.id, label);
}
let currentOffset = 8 + this.fn.mir.paramLocals.size * 8;
let frameSize = 0;
for (const local of this.fn.mir.paramLocals.values()) {
this.localOffsets.set(local.id, currentOffset);
currentOffset -= 8;
const reg = this.reg();
this.pushIns({ tag: "alloc_param", reg, size: 8 });
this.localRegs.set(local.id, reg);
}
// return address
currentOffset -= 8;
// old rbp
currentOffset -= 8;
// return value
this.localOffsets.set(this.fn.mir.returnLocal.id, currentOffset);
currentOffset -= 8;
frameSize += 8;
{
const reg = this.reg();
this.pushIns({ tag: "alloc_local", reg, size: 8 });
this.localRegs.set(this.fn.mir.returnLocal.id, reg);
}
for (const local of this.fn.mir.locals) {
if (this.localOffsets.has(local.id)) {
continue;
}
this.localOffsets.set(local.id, currentOffset);
currentOffset -= 8;
frameSize += 8;
}
for (const local of this.fn.mir.locals) {
if (this.localRegs.has(local.id)) {
continue;
@ -134,12 +108,6 @@ class FnGen {
this.localRegs.set(local.id, reg);
}
if (frameSize % 16 !== 8) {
frameSize += 8;
}
this.fn.frameSize = frameSize;
this.fn.localOffsets = this.localOffsets;
this.fn.localRegs = this.localRegs;
for (const block of this.fn.mir.blocks) {
@ -200,17 +168,17 @@ class FnGen {
}
case "load": {
const reg = this.reg();
const offset = this.localOffsets.get(k.local.id)!;
this.pushIns({ tag: "load", reg, offset });
const sReg = this.localRegs.get(k.local.id)!;
this.pushIns({ tag: "load", reg, sReg });
this.pushIns({ tag: "push", reg });
this.pushIns({ tag: "kill", reg });
return;
}
case "store": {
const reg = this.reg();
const offset = this.localOffsets.get(k.local.id)!;
const sReg = this.localRegs.get(k.local.id)!;
this.pushIns({ tag: "pop", reg });
this.pushIns({ tag: "store_reg", offset, reg });
this.pushIns({ tag: "store_reg", sReg, reg });
this.pushIns({ tag: "kill", reg });
return;
}

View File

@ -145,10 +145,10 @@ function eliminateMovIntStoreReg(fn: Fn) {
) {
throw new Error();
}
const offset = storeReg.ins.offset;
const sReg = storeReg.ins.sReg;
const val = movInt.ins.val;
fn.lines.splice(i + 1, 2);
fn.lines[i].ins = { tag: "store_imm", offset, val };
fn.lines[i].ins = { tag: "store_imm", sReg, val };
}
}
@ -223,8 +223,10 @@ function replaceReg(fn: Fn, cand: Reg, replacement: Reg) {
case "load":
case "store_reg":
ins.reg = r(ins.reg);
ins.sReg = r(ins.sReg);
break;
case "store_imm":
ins.sReg = r(ins.sReg);
break;
case "call_reg":
ins.reg = r(ins.reg);