1use llvm_ir::{Context, Function, Module};
10
11pub trait FunctionPass {
17 fn run_on_function(&mut self, ctx: &mut Context, func: &mut Function) -> bool;
19
20 fn name(&self) -> &'static str;
22}
23
24pub trait ModulePass {
26 fn run_on_module(&mut self, ctx: &mut Context, module: &mut Module) -> bool;
28
29 fn name(&self) -> &'static str;
31}
32
33pub struct FunctionPassAdapter<P: FunctionPass> {
39 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
59pub struct PassManager {
65 passes: Vec<Box<dyn ModulePass>>,
67}
68
69impl PassManager {
70 pub fn new() -> Self {
72 PassManager { passes: Vec::new() }
73 }
74
75 pub fn add_module_pass(&mut self, pass: impl ModulePass + 'static) {
77 self.passes.push(Box::new(pass));
78 }
79
80 pub fn add_function_pass(&mut self, pass: impl FunctionPass + 'static) {
82 self.passes.push(Box::new(FunctionPassAdapter { pass }));
83 }
84
85 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 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); }
187}