灵符lingfu

ling-mir

↓ 0 downloads

Mid-level IR with real optimization passes

Keywords mir, compiler, optimizer, ir
lingfu install ling-mir
LicenseApache-2.0 OR MIT
AuthorsLing Lin, Sanny Lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (29)

src/optimizations/inliner.rs
use crate::ir::*;
use rustc_hash::FxHashMap as HashMap;

pub struct Inliner;

impl Default for Inliner {
    fn default() -> Self {
        Self::new()
    }
}

impl Inliner {
    pub fn new() -> Self {
        Self
    }

    pub fn inline_function(
        &self,
        func: &mut MirFunction,
        fn_map: &HashMap<String, MirFunction>,
        max_depth: usize,
    ) {
        let mut changed = true;
        let mut current_depth = 0;

        while changed && current_depth < max_depth {
            changed = false;
            let mut i = 0;
            while i < func.basic_blocks.len() {
                let mut call_found = None;
                {
                    let bb = &func.basic_blocks[i];
                    for (stmt_idx, stmt) in bb.statements.iter().enumerate() {
                        if let StatementKind::Assign(
                            _,
                            Rvalue::Call {
                                func: Operand::Constant(Constant::Function(name)),
                                args,
                            },
                        ) = &stmt.kind
                        {
                            if name == &func.name {
                                continue;
                            }
                            if let Some(target_fn) = fn_map.get(name) {
                                let is_recursive = target_fn.basic_blocks.iter().any(|bb| {
                                    bb.statements.iter().any(|s| {
                                        matches!(&s.kind, StatementKind::Assign(_, Rvalue::Call {
                                                func: Operand::Constant(Constant::Function(n)), ..
                                            }) if n == &target_fn.name)
                                    })
                                });
                                if is_recursive {
                                    continue;
                                }
                                if target_fn.basic_blocks.len() < 100 {
                                    call_found = Some((stmt_idx, name.clone(), args.clone()));
                                    break;
                                }
                            }
                        }
                    }
                }

                if let Some((stmt_idx, target_name, args)) = call_found {
                    if let Some(target_fn) = fn_map.get(&target_name).cloned() {
                        self.perform_inline(func, i, stmt_idx, &target_fn, &args);
                        changed = true;
                        current_depth += 1;
                        break;
                    }
                }
                i += 1;
            }
        }
    }

    fn perform_inline(
        &self,
        caller: &mut MirFunction,
        bb_idx: usize,
        stmt_idx: usize,
        callee: &MirFunction,
        args: &[Operand],
    ) {
        let local_offset = caller.next_local();

        // The callee's flat local space — return slot, parameters, then its own
        // temporaries — is remapped contiguously onto fresh caller indices
        // starting at `local_offset`. Reserve declarations for every slot so the
        // return slot and parameters (which the callee does not store in
        // `locals`) get backing entries in the caller.
        for _ in 0..=callee.arg_count {
            caller.locals.push(LocalDecl {
                ty: MirType::Any,
                name: None,
                span: ling_ast::Span::DUMMY,
                is_mut: true,
                is_owning: true,
            });
        }
        for decl in &callee.locals {
            caller.locals.push(decl.clone());
        }

        let mut tail_statements = caller.basic_blocks[bb_idx].statements.split_off(stmt_idx);
        let call_stmt = tail_statements.remove(0);
        let ret_local = if let StatementKind::Assign(l, _) = call_stmt.kind {
            Some(l)
        } else {
            None
        };

        let tail_bb_id = BasicBlockId(caller.basic_blocks.len());
        let tail_bb = BasicBlock {
            statements: tail_statements,
            terminator: caller.basic_blocks[bb_idx].terminator.take(),
        };

        let block_offset = caller.basic_blocks.len() + 1;
        let mut callee_bb_map = HashMap::default();
        for (i, _) in callee.basic_blocks.iter().enumerate() {
            callee_bb_map.insert(BasicBlockId(i), BasicBlockId(block_offset + i));
        }

        caller.basic_blocks[bb_idx].terminator = Some(Terminator {
            kind: TerminatorKind::Goto { target: BasicBlockId(block_offset) },
            span: call_stmt.span,
        });

        let mut init_stmts = Vec::new();
        for (j, arg) in args.iter().enumerate() {
            let param_local = Local(local_offset + j + 1);
            init_stmts.push(Statement {
                kind: StatementKind::StorageLive(param_local),
                span: call_stmt.span,
            });
            init_stmts.push(Statement {
                kind: StatementKind::Assign(param_local, Rvalue::Use(arg.clone())),
                span: call_stmt.span,
            });
        }

        let callee_slots = callee.arg_count + 1 + callee.locals.len();
        for i in (callee.arg_count + 1)..callee_slots {
            init_stmts.push(Statement {
                kind: StatementKind::StorageLive(Local(local_offset + i)),
                span: call_stmt.span,
            });
        }
        init_stmts.push(Statement {
            kind: StatementKind::StorageLive(Local(local_offset)),
            span: call_stmt.span,
        });

        let mut translated_blocks = Vec::new();
        for (i, bb) in callee.basic_blocks.iter().enumerate() {
            let mut new_bb = bb.clone();

            for stmt in &mut new_bb.statements {
                self.remap_statement(stmt, local_offset);
            }

            if i == 0 {
                let mut combined = init_stmts.clone();
                combined.extend(new_bb.statements);
                new_bb.statements = combined;
            }

            if let Some(term) = &mut new_bb.terminator {
                match &mut term.kind {
                    TerminatorKind::Goto { target } => {
                        *target = *callee_bb_map.get(target).unwrap();
                    },
                    TerminatorKind::SwitchInt { discr, targets, otherwise } => {
                        self.remap_operand(discr, local_offset);
                        for (_, target) in targets {
                            *target = *callee_bb_map.get(target).unwrap();
                        }
                        *otherwise = *callee_bb_map.get(otherwise).unwrap();
                    },
                    TerminatorKind::Return => {
                        if let Some(dest) = ret_local {
                            new_bb.statements.retain(|s| {
                                !matches!(&s.kind, StatementKind::StorageDead(l) if l.0 == local_offset)
                            });
                            new_bb.statements.push(Statement {
                                kind: StatementKind::Assign(
                                    dest,
                                    Rvalue::Use(Operand::Copy(Local(local_offset))),
                                ),
                                span: term.span,
                            });
                        }
                        term.kind = TerminatorKind::Goto { target: tail_bb_id };
                    },
                    _ => {},
                }
            } else {
                new_bb.terminator = Some(Terminator {
                    kind: TerminatorKind::Goto { target: tail_bb_id },
                    span: call_stmt.span,
                });
            }
            translated_blocks.push(new_bb);
        }

        caller.basic_blocks.push(tail_bb);
        caller.basic_blocks.extend(translated_blocks);
    }

    fn remap_statement(&self, stmt: &mut Statement, offset: usize) {
        match &mut stmt.kind {
            StatementKind::Assign(l, rval) => {
                l.0 += offset;
                self.remap_rvalue(rval, offset);
            },
            StatementKind::SetAttr(obj, _, val) => {
                self.remap_operand(obj, offset);
                self.remap_operand(val, offset);
            },
            StatementKind::SetIndex(obj, idx, val) => {
                self.remap_operand(obj, offset);
                self.remap_operand(idx, offset);
                self.remap_operand(val, offset);
            },
            StatementKind::StorageLive(l)
            | StatementKind::StorageDead(l)
            | StatementKind::Drop(l) => {
                l.0 += offset;
            },
            StatementKind::VectorStore(obj, idx, val) => {
                self.remap_operand(obj, offset);
                self.remap_operand(idx, offset);
                self.remap_operand(val, offset);
            },
        }
    }

    fn remap_rvalue(&self, rval: &mut Rvalue, offset: usize) {
        match rval {
            Rvalue::Use(op) | Rvalue::UnaryOp(_, op) | Rvalue::GetAttr(op, _) => {
                self.remap_operand(op, offset);
            },
            Rvalue::BinaryOp(_, l, r) | Rvalue::GetIndex(l, r) => {
                self.remap_operand(l, offset);
                self.remap_operand(r, offset);
            },
            Rvalue::Call { func, args } => {
                self.remap_operand(func, offset);
                for arg in args {
                    self.remap_operand(arg, offset);
                }
            },
            Rvalue::Aggregate(_, ops) => {
                for op in ops {
                    self.remap_operand(op, offset);
                }
            },
            Rvalue::Ref(l) | Rvalue::MutRef(l) => {
                l.0 += offset;
            },
            Rvalue::VectorSplat(op, _) => self.remap_operand(op, offset),
            Rvalue::VectorLoad(obj, idx, _) => {
                self.remap_operand(obj, offset);
                self.remap_operand(idx, offset);
            },
            Rvalue::VectorFMA(a, b, c) => {
                self.remap_operand(a, offset);
                self.remap_operand(b, offset);
                self.remap_operand(c, offset);
            },
            Rvalue::InlineAsm(_) => {},
        }
    }

    fn remap_operand(&self, op: &mut Operand, offset: usize) {
        match op {
            Operand::Copy(l) | Operand::Move(l) => {
                l.0 += offset;
            },
            _ => {},
        }
    }
}

← Back to index