mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
compiler: add lir
This commit is contained in:
parent
432053bcaf
commit
8a8dce419e
@ -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() {
|
||||
|
@ -35,6 +35,10 @@ export class AstLowerer implements ast.Visitor {
|
||||
.toArray()
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
public toArray(): Fn[] {
|
||||
return this.fns.values().toArray();
|
||||
}
|
||||
}
|
||||
|
||||
export class FnLowerer {
|
||||
|
62
slige/compiler/middle/lir.ts
Normal file
62
slige/compiler/middle/lir.ts
Normal 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 };
|
@ -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;
|
||||
};
|
||||
|
259
slige/compiler/middle/mir_lower.ts
Normal file
259
slige/compiler/middle/mir_lower.ts
Normal 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);
|
||||
}
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
127
slige/compiler/stringify/lir.ts
Normal file
127
slige/compiler/stringify/lir.ts
Normal 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)!}`;
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
export * from "./hir.ts";
|
||||
export * from "./mir.ts";
|
||||
export * from "./lir.ts";
|
||||
|
Loading…
x
Reference in New Issue
Block a user