mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
compiler: generate asm
This commit is contained in:
parent
dcdfc32da6
commit
2025a450f6
244
backup-compiler/asm_gen.ts
Normal file
244
backup-compiler/asm_gen.ts
Normal file
@ -0,0 +1,244 @@
|
||||
import * as lir from "./lir.ts";
|
||||
|
||||
export class AsmGen {
|
||||
private writer = new AsmWriter();
|
||||
|
||||
private liveRegs = new Set<lir.Reg>();
|
||||
private regSelect = new Map<lir.Reg, string>();
|
||||
|
||||
public constructor(
|
||||
private lir: lir.Program,
|
||||
) {}
|
||||
|
||||
public generate(): string {
|
||||
this.writeln(`bits 64`);
|
||||
this.writeln(`section .data`);
|
||||
for (const [id, val] of this.lir.strings) {
|
||||
this.writeln(`align 8`);
|
||||
this.writeln(`sbc__string_${id}:`);
|
||||
this.writeIns(`dq ${val.length}`);
|
||||
const escaped = val
|
||||
.replaceAll('"', '\\"')
|
||||
.replaceAll("\n", "\\n")
|
||||
.replaceAll("\t", "\\t");
|
||||
this.writeIns(`db "${escaped}"`);
|
||||
}
|
||||
this.writeln(`section .text`);
|
||||
for (const fn of this.lir.fns) {
|
||||
this.writeln(`align 8`);
|
||||
this.writeln(`global ${fn.label}:`);
|
||||
this.writeln(`${fn.label}:`);
|
||||
|
||||
this.generateFnBody(fn);
|
||||
}
|
||||
|
||||
return this.writer.finalize();
|
||||
}
|
||||
|
||||
private generateFnBody(fn: lir.Fn) {
|
||||
const stmtKind = fn.mir.stmt.kind;
|
||||
if (stmtKind.tag !== "fn") {
|
||||
throw new Error();
|
||||
}
|
||||
if (stmtKind.attrs.at(0)?.ident === "c_function") {
|
||||
const arg = stmtKind.attrs.at(0)!.args.at(0);
|
||||
if (!arg || arg.kind.tag !== "string") {
|
||||
throw new Error("incorrect args for attribute");
|
||||
}
|
||||
const label = arg.kind.val;
|
||||
this.generateCFunctionBody(label, fn.mir.paramLocals.size);
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeIns(`push r12`);
|
||||
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:`);
|
||||
this.writeIns(`mov rsp, rbp`);
|
||||
this.writeIns(`pop rbp`);
|
||||
this.writeIns(`pop r12`);
|
||||
}
|
||||
|
||||
private generateCFunctionBody(label: string, args: number) {
|
||||
const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||
const returnReg = "rax";
|
||||
if (args > argRegs.length) {
|
||||
throw new Error(
|
||||
`arg count (${args}) > ${argRegs.length} not supported`,
|
||||
);
|
||||
}
|
||||
this.writeIns(`push ${returnReg}`);
|
||||
for (const reg of argRegs.slice(0, args + 1)) {
|
||||
this.writeIns(`push ${reg}`);
|
||||
}
|
||||
this.writeIns(`call ${label}`);
|
||||
this.writeIns(`mov r12, rax`);
|
||||
for (const reg of argRegs.slice(0, args + 1).toReversed()) {
|
||||
this.writeIns(`push ${reg}`);
|
||||
}
|
||||
this.writeIns(`pop ${returnReg}`);
|
||||
this.writeIns(`push r12`);
|
||||
}
|
||||
|
||||
private generateIns(ins: lir.Ins) {
|
||||
const r = (reg: lir.Reg) => this.reg(reg);
|
||||
|
||||
switch (ins.tag) {
|
||||
case "error":
|
||||
throw new Error();
|
||||
case "nop":
|
||||
this.writeIns(`nop`);
|
||||
return;
|
||||
case "mov_int":
|
||||
this.writeIns(`mov ${r(ins.reg)}, ${ins.val}`);
|
||||
return;
|
||||
case "mov_string":
|
||||
this.writeIns(`mov ${r(ins.reg)}, sbc__string_${ins.stringId}`);
|
||||
return;
|
||||
case "mov_fn":
|
||||
this.writeIns(`mov ${r(ins.reg)}, ${ins.fn.label}`);
|
||||
return;
|
||||
case "push":
|
||||
this.writeIns(`push ${r(ins.reg)}`);
|
||||
return;
|
||||
case "pop":
|
||||
this.writeIns(`pop ${r(ins.reg)}`);
|
||||
return;
|
||||
case "load":
|
||||
this.writeIns(`mov QWORD [rbp${ins.offset}], ${r(ins.reg)}`);
|
||||
return;
|
||||
case "store":
|
||||
this.writeIns(`mov ${r(ins.reg)}, QWORD [rbp${ins.offset}]`);
|
||||
return;
|
||||
case "call_reg":
|
||||
this.generateCall(r(ins.reg), ins.args);
|
||||
return;
|
||||
case "call_fn":
|
||||
this.generateCall(ins.fn.label, ins.args);
|
||||
return;
|
||||
case "jmp":
|
||||
this.writeIns(`jmp .L${ins.target}`);
|
||||
return;
|
||||
case "jnz_reg":
|
||||
this.writeIns(`jnz ${r(ins.reg)}, .L${ins.target}`);
|
||||
return;
|
||||
case "ret":
|
||||
this.writeIns(`jmp .exit`);
|
||||
return;
|
||||
case "lt":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`setle ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "eq":
|
||||
this.writeIns(`cmp ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
this.writeIns(`sete ${this.reg8(ins.dst)}`);
|
||||
return;
|
||||
case "add":
|
||||
this.writeIns(`add ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
return;
|
||||
case "mul":
|
||||
this.writeIns(`imul ${r(ins.dst)}, ${r(ins.src)}`);
|
||||
return;
|
||||
case "kill":
|
||||
this.kill(ins.reg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private generateCall(value: string, args: number) {
|
||||
const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
|
||||
const returnReg = "rax";
|
||||
if (args > argRegs.length) {
|
||||
throw new Error(
|
||||
`arg count (${args}) > ${argRegs.length} not supported`,
|
||||
);
|
||||
}
|
||||
this.writeIns(`push ${returnReg}`);
|
||||
for (const reg of argRegs.slice(0, args + 1)) {
|
||||
this.writeIns(`push ${reg}`);
|
||||
}
|
||||
this.writeIns(`call ${value}`);
|
||||
this.writeIns(`mov r12, rax`);
|
||||
for (const reg of argRegs.slice(0, args + 1).toReversed()) {
|
||||
this.writeIns(`push ${reg}`);
|
||||
}
|
||||
this.writeIns(`pop ${returnReg}`);
|
||||
this.writeIns(`push r12`);
|
||||
}
|
||||
|
||||
private reg(reg: lir.Reg): string {
|
||||
this.allocReg(reg);
|
||||
return this.regSelect.get(reg)!;
|
||||
}
|
||||
|
||||
private reg8(reg: lir.Reg): string {
|
||||
this.allocReg(reg);
|
||||
return {
|
||||
"rax": "al",
|
||||
"rdi": "dil",
|
||||
"rsi": "sil",
|
||||
"rdx": "dl",
|
||||
"rcx": "cl",
|
||||
"r8": "r8b",
|
||||
"r9": "r9b",
|
||||
"r10": "r10b",
|
||||
"r11": "r11b",
|
||||
}[this.regSelect.get(reg)!]!;
|
||||
}
|
||||
|
||||
private allocReg(reg: lir.Reg) {
|
||||
if (!this.liveRegs.has(reg)) {
|
||||
this.liveRegs.add(reg);
|
||||
const regSel = [
|
||||
"rax",
|
||||
"rdi",
|
||||
"rsi",
|
||||
"rdx",
|
||||
"rcx",
|
||||
"r8",
|
||||
"r9",
|
||||
"r10",
|
||||
"r11",
|
||||
].at(this.liveRegs.size);
|
||||
if (!regSel) {
|
||||
throw new Error("ran out of registers");
|
||||
}
|
||||
this.regSelect.set(reg, regSel);
|
||||
}
|
||||
}
|
||||
|
||||
private kill(reg: lir.Reg) {
|
||||
this.liveRegs.delete(reg);
|
||||
this.regSelect.delete(reg);
|
||||
}
|
||||
|
||||
private writeIns(ins: string) {
|
||||
this.writer.writeln(` ${ins}`);
|
||||
}
|
||||
|
||||
private writeln(line: string) {
|
||||
this.writer.writeln(line);
|
||||
}
|
||||
}
|
||||
|
||||
class AsmWriter {
|
||||
private result = "";
|
||||
|
||||
public writeln(line: string) {
|
||||
this.result += `${line}\n`;
|
||||
}
|
||||
|
||||
public finalize(): string {
|
||||
return this.result;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ export type Fn = {
|
||||
label: string;
|
||||
mir: mir.Fn;
|
||||
lines: Line[];
|
||||
frameSize: number;
|
||||
};
|
||||
|
||||
export type Line = {
|
||||
@ -27,12 +28,13 @@ export type Ins =
|
||||
| { tag: "pop"; reg: Reg }
|
||||
| { tag: "load"; reg: Reg; offset: number }
|
||||
| { tag: "store"; offset: number; reg: Reg }
|
||||
| { tag: "call_reg"; reg: Reg }
|
||||
| { tag: "call_fn"; fn: Fn }
|
||||
| { tag: "call_reg"; reg: Reg; args: number }
|
||||
| { tag: "call_fn"; fn: Fn; args: number }
|
||||
| { tag: "jmp"; target: Label }
|
||||
| { tag: "jnz_reg"; reg: Reg; target: Label }
|
||||
| { tag: "ret" }
|
||||
| { tag: "lt" | "eq" | "add" | "mul"; dst: Reg; src: Reg };
|
||||
| { tag: "lt" | "eq" | "add" | "mul"; dst: Reg; src: Reg }
|
||||
| { tag: "kill"; reg: Reg };
|
||||
|
||||
export type Reg = number;
|
||||
export type Label = number;
|
||||
@ -81,9 +83,9 @@ export class ProgramStringifyer {
|
||||
case "store":
|
||||
return `store ${ins.offset}, %${ins.reg}`;
|
||||
case "call_reg":
|
||||
return `call_reg %${ins.reg}`;
|
||||
return `call_reg %${ins.reg}, ${ins.args}`;
|
||||
case "call_fn":
|
||||
return `call_fn ${ins.fn.label}`;
|
||||
return `call_fn ${ins.fn.label}, ${ins.args}`;
|
||||
case "jmp":
|
||||
return `jmp .b${ins.target}`;
|
||||
case "jnz_reg":
|
||||
@ -95,6 +97,8 @@ export class ProgramStringifyer {
|
||||
case "add":
|
||||
case "mul":
|
||||
return `${ins.tag} %${ins.dst}, %${ins.src}`;
|
||||
case "kill":
|
||||
return `kill %${ins.reg}`;
|
||||
}
|
||||
const _: never = ins;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export class LirGen {
|
||||
const mir = this.mirGen.fnMir(stmt, stmt.kind);
|
||||
const id = this.fnIds++;
|
||||
const label = `sbc__${stmt.kind.ident}`;
|
||||
const fn: Fn = { id, label, mir, lines: [] };
|
||||
const fn: Fn = { id, label, mir, lines: [], frameSize: 0 };
|
||||
this.fns.set(id, fn);
|
||||
this.stmtFns.set(stmt.id, fn);
|
||||
}
|
||||
@ -43,15 +43,10 @@ export class LirGen {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
// if (stmtKind.attrs.at(0)?.ident === "c_function") {
|
||||
// const arg = stmtKind.attrs.at(0)!.args.at(0);
|
||||
// if (!arg || arg.kind.tag !== "string") {
|
||||
// throw new Error("incorrect args for attribute");
|
||||
// }
|
||||
// const label = arg.kind.val;
|
||||
// new CFunctionGen(fn, label).generate();
|
||||
// continue;
|
||||
// }
|
||||
if (stmtKind.attrs.at(0)?.ident === "c_function") {
|
||||
// No need to generate lir.
|
||||
continue;
|
||||
}
|
||||
|
||||
new FnGen(
|
||||
fn,
|
||||
@ -75,6 +70,7 @@ class FnGen {
|
||||
private currentLabels: Label[] = [];
|
||||
|
||||
private nextOffset = -8;
|
||||
private frameSize = 0;
|
||||
private localOffsets = new Map<number, number>();
|
||||
|
||||
public constructor(
|
||||
@ -91,7 +87,9 @@ class FnGen {
|
||||
for (const local of this.fn.mir.locals) {
|
||||
this.localOffsets.set(local.id, this.nextOffset);
|
||||
this.nextOffset -= 8;
|
||||
this.frameSize += 8;
|
||||
}
|
||||
this.fn.frameSize = this.frameSize;
|
||||
for (const block of this.fn.mir.blocks) {
|
||||
this.currentLabels.push(this.blockLabels.get(block.id)!);
|
||||
for (const stmt of block.stmts) {
|
||||
@ -117,12 +115,14 @@ class FnGen {
|
||||
const stringId = this.strings.intern(k.val.val);
|
||||
this.pushIns({ tag: "mov_string", reg, stringId });
|
||||
this.pushIns({ tag: "push", reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "int": {
|
||||
const reg = this.reg();
|
||||
this.pushIns({ tag: "mov_int", reg, val: k.val.val });
|
||||
this.pushIns({ tag: "push", reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "fn": {
|
||||
@ -133,6 +133,7 @@ class FnGen {
|
||||
fn: this.stmtFns.get(k.val.stmt.id)!,
|
||||
});
|
||||
this.pushIns({ tag: "push", reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -142,6 +143,7 @@ class FnGen {
|
||||
case "pop": {
|
||||
const reg = this.reg();
|
||||
this.pushIns({ tag: "pop", reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "load": {
|
||||
@ -149,6 +151,7 @@ class FnGen {
|
||||
const offset = this.localOffsets.get(k.local.id)!;
|
||||
this.pushIns({ tag: "load", reg, offset });
|
||||
this.pushIns({ tag: "push", reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "store": {
|
||||
@ -156,12 +159,14 @@ class FnGen {
|
||||
const offset = this.localOffsets.get(k.local.id)!;
|
||||
this.pushIns({ tag: "pop", reg });
|
||||
this.pushIns({ tag: "store", offset, reg });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "call": {
|
||||
const reg = this.reg();
|
||||
this.pushIns({ tag: "pop", reg });
|
||||
this.pushIns({ tag: "call_reg", reg });
|
||||
this.pushIns({ tag: "call_reg", reg, args: k.args });
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
return;
|
||||
}
|
||||
case "lt":
|
||||
@ -174,6 +179,8 @@ class FnGen {
|
||||
this.pushIns({ tag: "pop", reg: dst });
|
||||
this.pushIns({ tag: k.tag, dst, src });
|
||||
this.pushIns({ tag: "push", reg: dst });
|
||||
this.pushIns({ tag: "kill", reg: src });
|
||||
this.pushIns({ tag: "kill", reg: dst });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -206,6 +213,7 @@ class FnGen {
|
||||
reg,
|
||||
target: this.blockLabels.get(k.falsy.id)!,
|
||||
});
|
||||
this.pushIns({ tag: "kill", reg });
|
||||
this.pushIns({
|
||||
tag: "jmp",
|
||||
target: this.blockLabels.get(k.falsy.id)!,
|
||||
@ -227,16 +235,6 @@ class FnGen {
|
||||
}
|
||||
}
|
||||
|
||||
class CFunctionGen {
|
||||
public constructor(
|
||||
private fn: Fn,
|
||||
private label: string,
|
||||
) {}
|
||||
|
||||
public generate() {
|
||||
}
|
||||
}
|
||||
|
||||
class StringIntern {
|
||||
private ids = 0;
|
||||
private strings = new Map<number, string>();
|
||||
|
@ -4,6 +4,7 @@ import { MirGen } from "./mir_gen.ts";
|
||||
import { FnStringifyer } from "./mir.ts";
|
||||
import { LirGen } from "./lir_gen.ts";
|
||||
import { ProgramStringifyer } from "./lir.ts";
|
||||
import { AsmGen } from "./asm_gen.ts";
|
||||
|
||||
async function main() {
|
||||
const text = await Deno.readTextFile(Deno.args[0]);
|
||||
@ -29,6 +30,10 @@ async function main() {
|
||||
const lir = new LirGen(ast, mirGen).generate();
|
||||
console.log("=== LIR ===");
|
||||
console.log(new ProgramStringifyer(lir).stringify());
|
||||
|
||||
const asm = new AsmGen(lir).generate();
|
||||
console.log("=== ASM ===");
|
||||
console.log(asm);
|
||||
}
|
||||
|
||||
main();
|
||||
|
Loading…
x
Reference in New Issue
Block a user