compiler: add lir

This commit is contained in:
SimonFJ20 2025-03-03 15:18:46 +01:00
parent 432053bcaf
commit 8a8dce419e
9 changed files with 483 additions and 8 deletions

View File

@ -1,10 +1,10 @@
import * as path from "jsr:@std/path";
import { Parser } from "./parse/parser.ts";
import * as ast from "./ast/mod.ts";
import * as ast from "@slige/ast";
import { Ctx, File } from "@slige/common";
import { Resolver } from "./resolve/resolver.ts";
import { Checker } from "./check/checker.ts";
import { AstLowerer } from "./middle/ast_lower.ts";
import { ast_lower, mir_lower } from "@slige/middle";
import { HirStringifyer } from "@slige/stringify";
async function main() {
@ -53,7 +53,7 @@ export class PackCompiler {
console.error("error(s) occurred.");
Deno.exit(1);
}
const astLowerer = new AstLowerer(
const astLowerer = new ast_lower.AstLowerer(
this.ctx,
resols,
checker,
@ -65,6 +65,16 @@ export class PackCompiler {
Deno.exit(1);
}
console.log("=== MIR ===\n" + astLowerer.mirString());
if (this.ctx.errorOccured()) {
console.error("error(s) occurred. stopping...");
Deno.exit(1);
}
const mirLowerer = new mir_lower.MirLowerer(
this.ctx,
astLowerer.toArray(),
);
mirLowerer.lower();
console.log("=== LIR ===\n" + mirLowerer.lirString());
}
public enableDebug() {

View File

@ -35,6 +35,10 @@ export class AstLowerer implements ast.Visitor {
.toArray()
.join("\n");
}
public toArray(): Fn[] {
return this.fns.values().toArray();
}
}
export class FnLowerer {

View File

@ -0,0 +1,62 @@
import { IdBase, IdMap } from "@slige/common";
import * as mir from "./mir.ts";
import { BlockId } from "./mir.ts";
export type Fn = {
mirFn: mir.Fn;
blocks: IdMap<BlockId, Block>;
locals: IdMap<LocalId, Local>;
};
export type LocalId = IdBase & { readonly _: unique symbol };
export type Local = {
id: LocalId;
base: mir.LocalId;
version: number;
};
export type Block = {
id: mir.BlockId;
stmts: Stmt[];
ter: Ter;
};
export type Stmt = {
kind: StmtKind;
};
export type StmtKind =
| { tag: "error" }
| { tag: "assign"; local: LocalId; rval: RVal };
export type Ter = {
kind: TerKind;
};
export type TerKind =
| { tag: "error" }
| { tag: "goto"; target: BlockId }
| {
tag: "switch";
discr: RVal;
targets: SwitchTarget[];
otherwise: BlockId;
}
| { tag: "return" };
export type SwitchTarget = {
value: number;
target: BlockId;
};
export type PhiSource = {
local: LocalId;
branch: BlockId;
};
export type RVal =
| { tag: "error" }
| { tag: "phi"; sources: PhiSource[] }
| { tag: "use"; local: LocalId }
| { tag: "const"; val: mir.Const };

View File

@ -35,13 +35,15 @@ export type Stmt = {
export type StmtKind =
| { tag: "error" }
| { tag: "assign"; place: Place; rval: RVal }
| { tag: "assign" } & AssignStmt
| { tag: "fake_read"; place: Place }
| { tag: "deinit"; place: Place }
| { tag: "live"; local: LocalId }
| { tag: "dead"; local: LocalId }
| { tag: "mention"; place: Place };
export type AssignStmt = { place: Place; rval: RVal };
export type Ter = {
kind: TerKind;
};

View File

@ -0,0 +1,259 @@
import * as mir from "./mir.ts";
import * as lir from "./lir.ts";
import { Ctx, exhausted, IdMap, Ids, todo } from "@slige/common";
import { BlockId } from "./mir.ts";
import { LirFnStringifyer } from "@slige/stringify";
export class MirLowerer {
private lirFns: lir.Fn[] = [];
public constructor(
private ctx: Ctx,
private mirFns: mir.Fn[],
) {}
public lower() {
for (const fn of this.mirFns) {
this.lirFns.push(new FnLowerer(fn).lower());
}
}
public lirString(): string {
return this.lirFns
.values()
.map((fn) => new LirFnStringifyer(this.ctx).fn(fn))
.toArray()
.join("\n");
}
}
export class FnLowerer {
private localIds = new Ids<lir.LocalId>();
private locals = new IdMap<lir.LocalId, lir.Local>();
private localVersionCounter = new IdMap<mir.LocalId, number>();
private blocks = new IdMap<BlockId, lir.Block>();
private blockLocals = new IdMap<BlockId, IdMap<mir.LocalId, lir.Local>>();
private currentBlockId?: BlockId;
public constructor(
private fn: mir.Fn,
) {}
public lower(): lir.Fn {
for (const mirBlock of this.fn.blocks.values()) {
const lirBlock = this.lowerBlock(mirBlock);
this.blocks.set(lirBlock.id, lirBlock);
}
return {
mirFn: this.fn,
blocks: this.blocks,
locals: this.locals,
};
}
private lowerBlock(block: mir.Block): lir.Block {
this.currentBlockId = block.id;
this.blockLocals.set(block.id, new IdMap());
const stmts: lir.Stmt[] = [];
const locals = new IdMap<mir.LocalId, lir.Local>();
for (const mirId of this.fn.locals.keys()) {
const lirId = this.localIds.nextThenStep();
this.localVersionCounter.set(mirId, 0);
const local: lir.Local = {
id: lirId,
base: mirId,
version: block.id.rawId,
};
locals.set(mirId, local);
this.locals.set(lirId, local);
this.blockLocals.get(block.id)!.set(mirId, local);
}
const superBlocks = this.fn.blocks
.values()
.filter((b) => blockHasTarget(b, block.id))
.toArray();
if (block.id.rawId !== 0) {
for (const mirId of locals.keys()) {
const sources: lir.PhiSource[] = [];
for (const superBlock of superBlocks) {
const local = this.blockLocals.get(superBlock.id)!.get(
mirId,
)!;
sources.push({ branch: superBlock.id, local: local.id });
}
stmts.push(
this.stmt({
tag: "assign",
local: locals.get(mirId)!.id,
rval: { tag: "phi", sources },
}),
);
}
}
for (const stmt of block.stmts) {
stmts.push(...this.lowerStmt(stmt));
}
const [s1, ter] = this.lowerTer(block.terminator);
stmts.push(...s1);
return { id: block.id, stmts, ter };
}
private lowerStmt(stmt: mir.Stmt): lir.Stmt[] {
const k = stmt.kind;
switch (k.tag) {
case "error":
return [this.stmt({ tag: "error" })];
case "assign":
return this.lowerAssignStmt(stmt, k);
case "fake_read":
return [this.stmt({ tag: "error" })];
case "deinit":
return todo();
case "live":
return todo();
case "dead":
return todo();
case "mention":
return todo();
}
exhausted(k);
}
private lowerAssignStmt(stmt: mir.Stmt, kind: mir.AssignStmt): lir.Stmt[] {
if (kind.place.proj.length !== 0) {
return todo();
}
const [s1, rval] = this.lowerRVal(kind.rval);
const version = this.localVersionCounter.get(kind.place.local)!;
this.localVersionCounter.set(kind.place.local, version + 1);
const lirId = this.localIds.nextThenStep();
this.blockLocals
.get(this.currentBlockId!)!
.set(kind.place.local, {
id: lirId,
base: kind.place.local,
version,
});
return [...s1, this.stmt({ tag: "assign", local: lirId, rval })];
}
private lowerRVal(rval: mir.RVal): [lir.Stmt[], lir.RVal] {
switch (rval.tag) {
case "error":
return [[], { tag: "error" }];
case "use": {
switch (rval.operand.tag) {
case "error":
return [[], { tag: "error" }];
case "copy":
case "move":
return [[], {
tag: "use",
local: this.blockLocals
.get(this.currentBlockId!)!
.get(rval.operand.place.local)!.id,
}];
case "const":
return [[], {
tag: "const",
val: rval.operand.val,
}];
}
return exhausted(rval.operand);
}
case "repeat":
case "ref":
case "ptr":
case "binary":
case "unary":
return todo();
case "adt":
case "call":
case "builtin":
return todo();
}
exhausted(rval);
}
private lowerTer(ter: mir.Ter): [lir.Stmt[], lir.Ter] {
const tk = ter.kind;
switch (tk.tag) {
case "unset":
return [[], this.ter({ tag: "error" })];
case "goto":
return [[], this.ter({ tag: "goto", target: tk.target })];
case "switch": {
const [s1, discr] = this.lowerOperand(tk.discr);
return [
[...s1],
this.ter({
tag: "switch",
discr,
targets: tk.targets
.map(({ target, value }) => ({ target, value })),
otherwise: tk.otherwise,
}),
];
}
case "return":
return [[], this.ter({ tag: "return" })];
case "unreachable":
return [[], this.ter({ tag: "error" })];
case "drop":
return [[], this.ter({ tag: "error" })];
}
exhausted(tk);
}
private lowerOperand(operand: mir.Operand): [lir.Stmt[], lir.RVal] {
switch (operand.tag) {
case "error":
return [[], { tag: "error" }];
case "copy":
case "move":
return todo(operand.tag);
case "const":
return [[], { tag: "const", val: operand.val }];
}
exhausted(operand);
}
private stmt(kind: lir.StmtKind): lir.Stmt {
return { kind };
}
private ter(kind: lir.TerKind): lir.Ter {
return { kind };
}
}
function blockHasTarget(block: mir.Block, target: BlockId): boolean {
const tk = block.terminator.kind;
switch (tk.tag) {
case "unset":
return false;
case "goto":
return tk.target == target;
case "switch":
return tk.targets.some((st) => st.target === target) ||
tk.otherwise == target;
case "return":
return false;
case "unreachable":
return false;
case "drop":
return tk.target == target;
}
exhausted(tk);
}

View File

@ -1,2 +1,6 @@
export * as mir from "./mir.ts";
export * as lir from "./lir.ts";
export * as ast_lower from "./ast_lower.ts";
export * as mir_lower from "./mir_lower.ts";
export * from "./mir.ts";
export * from "./ast_lower.ts";

View File

@ -1,9 +1,15 @@
#[builtin(Hello)]
fn c_hello() -> int {}
// #[builtin(Hello)]
// fn c_hello() -> int {}
fn main() {
c_hello();
let mut a = 2;
if true {
a = 3;
} else {
}
let b = a;
}

View File

@ -0,0 +1,127 @@
import { BlockId, lir, mir } from "@slige/middle";
import { Ctx, exhausted, IdMap, todo } from "@slige/common";
import { Ty, tyToString } from "@slige/ty";
export class LirFnStringifyer {
private blockIds = new IdMap<BlockId, number>();
private localIds = new IdMap<lir.LocalId, number>();
public constructor(
private ctx: Ctx,
) {}
public fn(fn: lir.Fn): string {
for (
const [idx, id] of fn.blocks
.keys()
.toArray()
.entries()
) {
this.blockIds.set(id, idx);
}
for (
const [idx, id] of fn.locals
.keys()
.toArray()
.entries()
) {
this.localIds.set(id, idx);
}
const blocks = fn.blocks
.values()
.toArray()
.map((block) => this.block(block))
.join("\n");
return `fn ${fn.mirFn.label} {\n${blocks}\n}`
.replaceAll("#", " ");
}
private block(block: lir.Block): string {
const id = this.blockIds.get(block.id);
return `#.b${id}: {\n${
[
...block.stmts
.map((stmt) => this.stmt(stmt)),
this.ter(block.ter),
]
.join("\n")
}\n#}`;
}
private stmt(stmt: lir.Stmt): string {
const k = stmt.kind;
switch (k.tag) {
case "error":
return "##<error>;";
case "assign":
return `##${this.local(k.local)} = ${this.rval(k.rval)}`;
}
exhausted(k);
}
private ter(ter: lir.Ter): string {
const k = ter.kind;
switch (k.tag) {
case "error":
return "##<error>;";
case "goto":
return `##goto ${this.blockId(k.target)}`;
case "switch": {
const discr = this.rval(k.discr);
const targets = k.targets
.map((target) =>
`\n###${target.value} => ${this.blockId(target.target)}`
)
.join("");
const otherwise = this.blockId(k.otherwise);
return `##switch ${discr}${targets}\n###_ => ${otherwise}`;
}
case "return":
return `##return;`;
}
exhausted(k);
}
private rval(rval: lir.RVal): string {
switch (rval.tag) {
case "error":
return "<error>";
case "phi":
return `phi [${
rval.sources
.map((src) =>
`(${this.blockId(src.branch)}, ${
this.local(src.local)
})`
)
.join(",")
}]`;
case "use":
return this.local(rval.local);
case "const":
return `${this.constVal(rval.val)}`;
}
}
private constVal(val: mir.Const): string {
switch (val.tag) {
case "null":
return `null`;
case "int":
return `${val.value}`;
case "str":
return `"${val.value}"`;
case "fn":
return `${val.item.ident.text}`;
}
exhausted(val);
}
private blockId(id: BlockId): string {
return `.b${this.blockIds.get(id)!}`;
}
private local(local: lir.LocalId): string {
return `%${this.localIds.get(local)!}`;
}
}

View File

@ -1,2 +1,3 @@
export * from "./hir.ts";
export * from "./mir.ts";
export * from "./lir.ts";