diff --git a/slige/compiler/main.ts b/slige/compiler/main.ts index 765a2de..fcb1cae 100644 --- a/slige/compiler/main.ts +++ b/slige/compiler/main.ts @@ -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() { diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts index a540f07..d69efbe 100644 --- a/slige/compiler/middle/ast_lower.ts +++ b/slige/compiler/middle/ast_lower.ts @@ -35,6 +35,10 @@ export class AstLowerer implements ast.Visitor { .toArray() .join("\n"); } + + public toArray(): Fn[] { + return this.fns.values().toArray(); + } } export class FnLowerer { diff --git a/slige/compiler/middle/lir.ts b/slige/compiler/middle/lir.ts new file mode 100644 index 0000000..4e1e5f3 --- /dev/null +++ b/slige/compiler/middle/lir.ts @@ -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; + locals: IdMap; +}; + +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 }; diff --git a/slige/compiler/middle/mir.ts b/slige/compiler/middle/mir.ts index ec5763e..4984eaf 100644 --- a/slige/compiler/middle/mir.ts +++ b/slige/compiler/middle/mir.ts @@ -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; }; diff --git a/slige/compiler/middle/mir_lower.ts b/slige/compiler/middle/mir_lower.ts new file mode 100644 index 0000000..ed9d276 --- /dev/null +++ b/slige/compiler/middle/mir_lower.ts @@ -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(); + private locals = new IdMap(); + + private localVersionCounter = new IdMap(); + + private blocks = new IdMap(); + + private blockLocals = new IdMap>(); + + 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(); + 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); +} diff --git a/slige/compiler/middle/mod.ts b/slige/compiler/middle/mod.ts index 353fac5..9002460 100644 --- a/slige/compiler/middle/mod.ts +++ b/slige/compiler/middle/mod.ts @@ -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"; diff --git a/slige/compiler/program.slg b/slige/compiler/program.slg index ce8a38b..7905a9b 100644 --- a/slige/compiler/program.slg +++ b/slige/compiler/program.slg @@ -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; } diff --git a/slige/compiler/stringify/lir.ts b/slige/compiler/stringify/lir.ts new file mode 100644 index 0000000..e8460ca --- /dev/null +++ b/slige/compiler/stringify/lir.ts @@ -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(); + private localIds = new IdMap(); + + 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 "##;"; + 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 "##;"; + 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 ""; + 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)!}`; + } +} diff --git a/slige/compiler/stringify/mod.ts b/slige/compiler/stringify/mod.ts index 28b54e5..eb237de 100644 --- a/slige/compiler/stringify/mod.ts +++ b/slige/compiler/stringify/mod.ts @@ -1,2 +1,3 @@ export * from "./hir.ts"; export * from "./mir.ts"; +export * from "./lir.ts";