import { Mir, Op, RValue, visitBlockSrcs } from "./mir.ts";

export function eliminateTransientVals(mir: Mir) {
    for (const fn of mir.fns) {
        for (const block of fn.blocks) {
            const cands: { src: RValue; consumer: Op; definition: Op }[] = [];

            visitBlockSrcs(block, (src, op, i) => {
                if (src.type !== "local") {
                    return;
                }
                const found = block.ops.find((op, fi) =>
                    op.kind.type === "assign" &&
                    op.kind.dst === src.id &&
                    fi < i!
                );
                if (!found) {
                    return;
                }
                cands.push({ src, consumer: op!, definition: found! });
            });

            //console.log(cands);

            for (const { src: oldsrc, consumer, definition } of cands) {
                if (oldsrc.type !== "local") {
                    throw new Error();
                }
                if (definition.kind.type !== "assign") {
                    throw new Error();
                }
                const src = definition.kind.src;

                const k = consumer.kind;
                switch (k.type) {
                    case "error":
                        break;
                    case "assign":
                        k.src = src;
                        break;
                    case "field":
                        if (same(k.subject, oldsrc)) {
                            k.subject = src;
                        }
                        break;
                    case "assign_field":
                        if (same(k.subject, oldsrc)) {
                            k.subject = src;
                        }
                        if (same(k.src, oldsrc)) {
                            k.src = src;
                        }
                        break;
                    case "index":
                        if (same(k.subject, oldsrc)) {
                            k.subject = src;
                        }
                        if (same(k.index, oldsrc)) {
                            k.index = src;
                        }
                        break;
                    case "assign_index":
                        if (same(k.subject, oldsrc)) {
                            k.subject = src;
                        }
                        if (same(k.index, oldsrc)) {
                            k.index = src;
                        }
                        if (same(k.src, oldsrc)) {
                            k.src = src;
                        }
                        break;
                    case "call_val":
                        if (same(k.subject, oldsrc)) {
                            k.subject = src;
                        }
                        for (let i = 0; i < k.args.length; ++i) {
                            if (same(k.args[i], oldsrc)) {
                                k.args[i] = src;
                            }
                        }
                        break;
                    case "binary":
                        if (same(k.left, oldsrc)) {
                            k.left = src;
                        }
                        if (same(k.right, oldsrc)) {
                            k.right = src;
                        }
                        break;
                }
            }
        }
    }
}

function same(a: RValue, b: RValue): boolean {
    return a.type === "local" && a.type === b.type && a.id === b.id;
}
