mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
compiler: optimize mir
This commit is contained in:
parent
be04fa1b6e
commit
ad06f80f72
@ -132,13 +132,15 @@ export class AsmGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private generateFnBody(fn: lir.Fn) {
|
private generateFnBody(fn: lir.Fn) {
|
||||||
|
let bodyIdx = 0;
|
||||||
const allocator = new StackAllocator();
|
const allocator = new StackAllocator();
|
||||||
for (const { ins } of fn.lines) {
|
for (const [i, { ins }] of fn.lines.entries()) {
|
||||||
if (ins.tag === "alloc_param") {
|
if (ins.tag === "alloc_param") {
|
||||||
allocator.allocParam(ins.reg, ins.size);
|
allocator.allocParam(ins.reg, ins.size);
|
||||||
} else if (ins.tag === "alloc_local") {
|
} else if (ins.tag === "alloc_local") {
|
||||||
allocator.allocLocal(ins.reg, ins.size);
|
allocator.allocLocal(ins.reg, ins.size);
|
||||||
} else {
|
} else {
|
||||||
|
bodyIdx = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +156,7 @@ export class AsmGen {
|
|||||||
this.writeIns(`sub rsp, ${this.layout.frameSize}`);
|
this.writeIns(`sub rsp, ${this.layout.frameSize}`);
|
||||||
this.writeIns(`jmp .L${fn.mir.entry.id}`);
|
this.writeIns(`jmp .L${fn.mir.entry.id}`);
|
||||||
|
|
||||||
for (const line of fn.lines) {
|
for (const line of fn.lines.slice(bodyIdx)) {
|
||||||
for (const label of line.labels) {
|
for (const label of line.labels) {
|
||||||
this.writeln(`.L${label}:`);
|
this.writeln(`.L${label}:`);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import { MirGen } from "./mir_gen.ts";
|
import { MirGen } from "./mir_gen.ts";
|
||||||
import * as ast from "./ast.ts";
|
import * as ast from "./ast.ts";
|
||||||
import * as mir from "./mir.ts";
|
import * as mir from "./mir.ts";
|
||||||
|
import { optimizeMirFn } from "./mir_optimize.ts";
|
||||||
|
|
||||||
export class LirGen {
|
export class LirGen {
|
||||||
private strings = new StringIntern();
|
private strings = new StringIntern();
|
||||||
@ -29,6 +30,7 @@ export class LirGen {
|
|||||||
throw new Error("only functions can compile top level");
|
throw new Error("only functions can compile top level");
|
||||||
}
|
}
|
||||||
const mir = this.mirGen.fnMir(stmt, stmt.kind);
|
const mir = this.mirGen.fnMir(stmt, stmt.kind);
|
||||||
|
optimizeMirFn(mir);
|
||||||
const id = this.fnIds++;
|
const id = this.fnIds++;
|
||||||
const label = `sbc__${stmt.kind.ident}`;
|
const label = `sbc__${stmt.kind.ident}`;
|
||||||
const fn: Fn = {
|
const fn: Fn = {
|
||||||
|
@ -8,18 +8,17 @@ import {
|
|||||||
Reg,
|
Reg,
|
||||||
} from "./lir.ts";
|
} from "./lir.ts";
|
||||||
|
|
||||||
export function lirOptimize(program: Program) {
|
export function optimizeLir(program: Program) {
|
||||||
console.log("=== BEFORE OPTIMIZATION ===");
|
//console.log("=== BEFORE OPTIMIZATION ===");
|
||||||
console.log(new ProgramStringifyer(program).stringify());
|
//console.log(new ProgramStringifyer(program).stringify());
|
||||||
|
|
||||||
let changed = true;
|
|
||||||
let sizeBefore = program.fns
|
let sizeBefore = program.fns
|
||||||
.reduce((acc, fn) => acc + fn.lines.length, 0);
|
.reduce((acc, fn) => acc + fn.lines.length, 0);
|
||||||
|
|
||||||
const sizeHistory = new Set([sizeBefore]);
|
const sizeHistory = new Set([sizeBefore]);
|
||||||
let repeats = 0;
|
let repeats = 0;
|
||||||
|
|
||||||
while (changed && repeats < 3) {
|
while (repeats < 1) {
|
||||||
for (const fn of program.fns) {
|
for (const fn of program.fns) {
|
||||||
eliminatePushPop(fn);
|
eliminatePushPop(fn);
|
||||||
eliminateMovFnCall(fn);
|
eliminateMovFnCall(fn);
|
||||||
@ -28,9 +27,6 @@ export function lirOptimize(program: Program) {
|
|||||||
}
|
}
|
||||||
const sizeAfter = program.fns
|
const sizeAfter = program.fns
|
||||||
.reduce((acc, fn) => acc + fn.lines.length, 0);
|
.reduce((acc, fn) => acc + fn.lines.length, 0);
|
||||||
if (sizeAfter !== sizeBefore) {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
sizeBefore = sizeAfter;
|
sizeBefore = sizeAfter;
|
||||||
if (sizeHistory.has(sizeBefore)) {
|
if (sizeHistory.has(sizeBefore)) {
|
||||||
repeats += 1;
|
repeats += 1;
|
||||||
@ -38,8 +34,8 @@ export function lirOptimize(program: Program) {
|
|||||||
sizeHistory.add(sizeBefore);
|
sizeHistory.add(sizeBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("=== AFTER OPTIMIZATION ===");
|
//console.log("=== AFTER OPTIMIZATION ===");
|
||||||
console.log(new ProgramStringifyer(program).stringify());
|
//console.log(new ProgramStringifyer(program).stringify());
|
||||||
}
|
}
|
||||||
|
|
||||||
function eliminatePushPop(fn: Fn) {
|
function eliminatePushPop(fn: Fn) {
|
||||||
|
@ -5,7 +5,7 @@ import { FnStringifyer } from "./mir.ts";
|
|||||||
import { LirGen } from "./lir_gen.ts";
|
import { LirGen } from "./lir_gen.ts";
|
||||||
import { ProgramStringifyer } from "./lir.ts";
|
import { ProgramStringifyer } from "./lir.ts";
|
||||||
import { AsmGen } from "./asm_gen.ts";
|
import { AsmGen } from "./asm_gen.ts";
|
||||||
import { lirOptimize } from "./lir_optimize.ts";
|
import { optimizeLir } from "./lir_optimize.ts";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const text = await Deno.readTextFile(Deno.args[0]);
|
const text = await Deno.readTextFile(Deno.args[0]);
|
||||||
@ -32,7 +32,7 @@ async function main() {
|
|||||||
// console.log("=== LIR ===");
|
// console.log("=== LIR ===");
|
||||||
// console.log(new ProgramStringifyer(lir).stringify());
|
// console.log(new ProgramStringifyer(lir).stringify());
|
||||||
|
|
||||||
lirOptimize(lir);
|
optimizeLir(lir);
|
||||||
|
|
||||||
const asm = new AsmGen(lir).generate();
|
const asm = new AsmGen(lir).generate();
|
||||||
// console.log("=== ASM ===");
|
// console.log("=== ASM ===");
|
||||||
|
@ -73,7 +73,11 @@ export class FnStringifyer {
|
|||||||
}
|
}
|
||||||
return `${kind.ident}:\n${
|
return `${kind.ident}:\n${
|
||||||
this.fn.locals
|
this.fn.locals
|
||||||
.map((local) => ` %${local.id}: ${tyToString(local.ty)}\n`)
|
.map((local) =>
|
||||||
|
` %${local.id} :: ${tyToString(local.ty)}${
|
||||||
|
local.ident ? ` '${local.ident}'` : ""
|
||||||
|
}\n`
|
||||||
|
)
|
||||||
.join("")
|
.join("")
|
||||||
}${
|
}${
|
||||||
this.fn.blocks
|
this.fn.blocks
|
||||||
|
97
sbc/mir_optimize.ts
Normal file
97
sbc/mir_optimize.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import * as ast from "./ast.ts";
|
||||||
|
import { Block, Fn, FnStringifyer } from "./mir.ts";
|
||||||
|
|
||||||
|
export function optimizeMirFn(fn: Fn) {
|
||||||
|
//console.log(`=== OPTIMIZING ${(fn.stmt.kind as ast.FnStmt).ident} ===`);
|
||||||
|
//console.log("=== BEFORE OPTIMIZATION ===");
|
||||||
|
//console.log(new FnStringifyer(fn).stringify());
|
||||||
|
|
||||||
|
const blockSize = fn.blocks
|
||||||
|
.map((block) => block.stmts.length)
|
||||||
|
.toSorted()
|
||||||
|
.at(-1)! + 1;
|
||||||
|
let sizeBefore = fnSize(fn, blockSize);
|
||||||
|
|
||||||
|
const sizeHistory = new Set([sizeBefore]);
|
||||||
|
let repeats = 0;
|
||||||
|
|
||||||
|
while (repeats < 1) {
|
||||||
|
eliminateUnreachable(fn);
|
||||||
|
joinSequentialBlocks(fn);
|
||||||
|
|
||||||
|
const sizeAfter = fnSize(fn, blockSize);
|
||||||
|
sizeBefore = sizeAfter;
|
||||||
|
if (sizeHistory.has(sizeBefore)) {
|
||||||
|
repeats += 1;
|
||||||
|
}
|
||||||
|
sizeHistory.add(sizeBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("=== AFTER OPTIMIZATION ===");
|
||||||
|
//console.log(new FnStringifyer(fn).stringify());
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnSize(fn: Fn, blockSize: number): number {
|
||||||
|
return fn.blocks
|
||||||
|
.reduce((acc, block) => acc + blockSize + block.stmts.length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eliminateUnreachable(fn: Fn) {
|
||||||
|
const toRemove = new Set<number>();
|
||||||
|
|
||||||
|
for (const block of fn.blocks) {
|
||||||
|
const preds = cfgPredecessors(fn, block);
|
||||||
|
|
||||||
|
if (block.id === fn.entry.id || preds.length !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toRemove.add(block.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.blocks = fn.blocks
|
||||||
|
.filter((block) => !toRemove.has(block.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinSequentialBlocks(fn: Fn) {
|
||||||
|
const toRemove = new Set<number>();
|
||||||
|
|
||||||
|
for (const first of fn.blocks) {
|
||||||
|
const firstSuccs = cfgSuccessors(first);
|
||||||
|
if (firstSuccs.length !== 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const [second] = firstSuccs;
|
||||||
|
const secondPreds = cfgPredecessors(fn, second);
|
||||||
|
if (secondPreds.length !== 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
first.stmts.push(...second.stmts);
|
||||||
|
first.ter = second.ter;
|
||||||
|
toRemove.add(second.id);
|
||||||
|
if (second.id === fn.exit.id) {
|
||||||
|
fn.exit = first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.blocks = fn.blocks
|
||||||
|
.filter((block) => !toRemove.has(block.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cfgPredecessors(fn: Fn, block: Block): Block[] {
|
||||||
|
return fn.blocks
|
||||||
|
.filter((b) => cfgSuccessors(b).some((s) => s.id === block.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cfgSuccessors(block: Block): Block[] {
|
||||||
|
const tk = block.ter.kind;
|
||||||
|
switch (tk.tag) {
|
||||||
|
case "error":
|
||||||
|
case "unset":
|
||||||
|
case "return":
|
||||||
|
return [];
|
||||||
|
case "goto":
|
||||||
|
return [tk.target];
|
||||||
|
case "if":
|
||||||
|
return [tk.truthy, tk.falsy];
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ export function tyToString(ty: Ty): string {
|
|||||||
case "fn": {
|
case "fn": {
|
||||||
const k = ty.stmt.kind as ast.StmtKind & { tag: "fn" };
|
const k = ty.stmt.kind as ast.StmtKind & { tag: "fn" };
|
||||||
const params = ty.params
|
const params = ty.params
|
||||||
.map((param, i) => `${k.params[i]}: ${tyToString(param)}`)
|
.map((param, i) => `${k.params[i].ident}: ${tyToString(param)}`)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
const returnTy = tyToString(ty.returnTy);
|
const returnTy = tyToString(ty.returnTy);
|
||||||
return `fn ${k.ident}(${params}) -> ${returnTy}`;
|
return `fn ${k.ident}(${params}) -> ${returnTy}`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user