Skip to main content

llvm_transforms/
pass.rs

1//! Pass infrastructure: FunctionPass, ModulePass, and PassManager.
2//!
3//! A `FunctionPass` transforms a single `Function` in isolation.
4//! A `ModulePass` transforms the whole `Module` (needed for inter-procedural
5//! passes such as inlining).
6//!
7//! `PassManager` sequences module passes and runs them to a fixed point.
8
9use llvm_ir::{Context, Function, Module};
10
11// ---------------------------------------------------------------------------
12// Traits
13// ---------------------------------------------------------------------------
14
15/// A pass that transforms a single function.
16pub trait FunctionPass {
17    /// Apply this pass to `func`. Returns `true` if the IR was modified.
18    fn run_on_function(&mut self, ctx: &mut Context, func: &mut Function) -> bool;
19
20    /// Human-readable name used in diagnostics.
21    fn name(&self) -> &'static str;
22}
23
24/// A pass that transforms an entire module.
25pub trait ModulePass {
26    /// Apply this pass to `module`. Returns `true` if the IR was modified.
27    fn run_on_module(&mut self, ctx: &mut Context, module: &mut Module) -> bool;
28
29    /// Human-readable name used in diagnostics.
30    fn name(&self) -> &'static str;
31}
32
33// ---------------------------------------------------------------------------
34// FunctionPassAdapter: lifts a FunctionPass into a ModulePass
35// ---------------------------------------------------------------------------
36
37/// Applies a `FunctionPass` to every non-declaration function in the module.
38pub struct FunctionPassAdapter<P: FunctionPass> {
39    /// Public API for `pass`.
40    pub pass: P,
41}
42
43impl<P: FunctionPass> ModulePass for FunctionPassAdapter<P> {
44    fn run_on_module(&mut self, ctx: &mut Context, module: &mut Module) -> bool {
45        let mut changed = false;
46        for i in 0..module.functions.len() {
47            if !module.functions[i].is_declaration {
48                changed |= self.pass.run_on_function(ctx, &mut module.functions[i]);
49            }
50        }
51        changed
52    }
53
54    fn name(&self) -> &'static str {
55        self.pass.name()
56    }
57}
58
59// ---------------------------------------------------------------------------
60// PassManager
61// ---------------------------------------------------------------------------
62
63/// Sequences module passes and runs them once, or to a fixed point.
64pub struct PassManager {
65    // `passes` field.
66    passes: Vec<Box<dyn ModulePass>>,
67}
68
69impl PassManager {
70    /// Public API for `new`.
71    pub fn new() -> Self {
72        PassManager { passes: Vec::new() }
73    }
74
75    /// Add a module-level pass.
76    pub fn add_module_pass(&mut self, pass: impl ModulePass + 'static) {
77        self.passes.push(Box::new(pass));
78    }
79
80    /// Add a function-level pass (wrapped automatically in a `FunctionPassAdapter`).
81    pub fn add_function_pass(&mut self, pass: impl FunctionPass + 'static) {
82        self.passes.push(Box::new(FunctionPassAdapter { pass }));
83    }
84
85    /// Run all passes over `module` once, in order.
86    ///
87    /// Returns `true` if any pass modified the IR.
88    pub fn run(&mut self, ctx: &mut Context, module: &mut Module) -> bool {
89        let mut changed = false;
90        for pass in &mut self.passes {
91            changed |= pass.run_on_module(ctx, module);
92        }
93        changed
94    }
95
96    /// Run all passes repeatedly until the IR stabilises or `max_iter` is reached.
97    pub fn run_until_fixed_point(
98        &mut self,
99        ctx: &mut Context,
100        module: &mut Module,
101        max_iter: usize,
102    ) {
103        for _ in 0..max_iter {
104            if !self.run(ctx, module) {
105                break;
106            }
107        }
108    }
109}
110
111impl Default for PassManager {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use llvm_ir::{BasicBlock, Function, InstrKind, Instruction, Linkage, Module};
121
122    struct NoOpPass;
123    impl FunctionPass for NoOpPass {
124        fn name(&self) -> &'static str {
125            "noop"
126        }
127        fn run_on_function(&mut self, _ctx: &mut Context, _func: &mut Function) -> bool {
128            false
129        }
130    }
131
132    fn make_one_func_module(ctx: &mut Context) -> Module {
133        let fn_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
134        let mut func = Function::new("f", fn_ty, vec![], Linkage::External);
135        let mut bb = BasicBlock::new("entry");
136        let iid = func.alloc_instr(Instruction {
137            name: None,
138            ty: ctx.void_ty,
139            kind: InstrKind::Unreachable,
140        });
141        bb.set_terminator(iid);
142        func.add_block(bb);
143        let mut m = Module::new("test");
144        m.add_function(func);
145        m
146    }
147
148    #[test]
149    fn noop_returns_false() {
150        let mut ctx = Context::new();
151        let mut module = make_one_func_module(&mut ctx);
152        let mut pm = PassManager::new();
153        pm.add_function_pass(NoOpPass);
154        assert!(!pm.run(&mut ctx, &mut module));
155    }
156
157    #[test]
158    fn fixed_point_stops_when_stable() {
159        let mut ctx = Context::new();
160        let mut module = make_one_func_module(&mut ctx);
161        let mut pm = PassManager::new();
162        pm.add_function_pass(NoOpPass);
163        pm.run_until_fixed_point(&mut ctx, &mut module, 100);
164    }
165
166    #[test]
167    fn declaration_is_skipped() {
168        let mut ctx = Context::new();
169        let fn_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
170        let decl = Function::new_declaration("ext", fn_ty, vec![], Linkage::External);
171        let mut module = Module::new("test");
172        module.add_function(decl);
173
174        struct PanicPass;
175        impl FunctionPass for PanicPass {
176            fn name(&self) -> &'static str {
177                "panic"
178            }
179            fn run_on_function(&mut self, _: &mut Context, _: &mut Function) -> bool {
180                panic!("must not run on a declaration");
181            }
182        }
183        let mut pm = PassManager::new();
184        pm.add_function_pass(PanicPass);
185        pm.run(&mut ctx, &mut module); // must not panic
186    }
187}