llvm_plugin/
pass_builder.rs

1use std::ffi::c_void;
2
3use inkwell_internals::llvm_versions;
4
5use super::{
6    FunctionAnalysisManager, FunctionPassManager, ModuleAnalysisManager, ModulePassManager,
7};
8
9/// Main struct for registering callbacks.
10pub struct PassBuilder {
11    inner: *mut c_void,
12}
13
14impl PassBuilder {
15    #[doc(hidden)]
16    pub unsafe fn from_raw(pass_builder: *mut c_void) -> Self {
17        Self {
18            inner: pass_builder,
19        }
20    }
21
22    /// Register a new pipeline parsing callback.
23    ///
24    /// These callbacks can be used to parse a single pass name, and populate
25    /// the given [ModulePassManager] accordingly.
26    pub fn add_module_pipeline_parsing_callback<T>(&mut self, cb: T)
27    where
28        T: Fn(&str, &mut ModulePassManager) -> PipelineParsing + 'static,
29    {
30        let cb = Box::new(cb);
31
32        extern "C" fn callback_deleter<T>(cb: *const c_void) {
33            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
34        }
35
36        extern "C" fn callback_entrypoint<T>(
37            cb: *const c_void,
38            name_ptr: *const u8,
39            name_len: usize,
40            manager: *mut c_void,
41        ) -> bool
42        where
43            T: Fn(&str, &mut ModulePassManager) -> PipelineParsing + 'static,
44        {
45            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
46            let name = unsafe { std::slice::from_raw_parts(name_ptr, name_len) };
47            let name = unsafe { std::str::from_utf8_unchecked(name) };
48            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
49
50            let res = cb(name, &mut manager);
51
52            Box::into_raw(cb);
53            matches!(res, PipelineParsing::Parsed)
54        }
55
56        unsafe {
57            super::passBuilderAddModulePipelineParsingCallback(
58                self.inner,
59                Box::into_raw(cb).cast(),
60                callback_deleter::<T>,
61                callback_entrypoint::<T>,
62            )
63        }
64    }
65
66    /// Register a new pipeline parsing callback.
67    ///
68    /// These callbacks can be used to parse a single pass name, and populate
69    /// the given [FunctionPassManager] accordingly.
70    pub fn add_function_pipeline_parsing_callback<T>(&mut self, cb: T)
71    where
72        T: Fn(&str, &mut FunctionPassManager) -> PipelineParsing + 'static,
73    {
74        let cb = Box::new(cb);
75
76        extern "C" fn callback_deleter<T>(cb: *const c_void) {
77            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
78        }
79
80        extern "C" fn callback_entrypoint<T>(
81            cb: *const c_void,
82            name_ptr: *const u8,
83            name_len: usize,
84            manager: *mut c_void,
85        ) -> bool
86        where
87            T: Fn(&str, &mut FunctionPassManager) -> PipelineParsing + 'static,
88        {
89            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
90            let name = unsafe { std::slice::from_raw_parts(name_ptr, name_len) };
91            let name = unsafe { std::str::from_utf8_unchecked(name) };
92            let mut manager = unsafe { FunctionPassManager::from_raw(manager) };
93
94            let res = cb(name, &mut manager);
95
96            Box::into_raw(cb);
97            matches!(res, PipelineParsing::Parsed)
98        }
99
100        unsafe {
101            super::passBuilderAddFunctionPipelineParsingCallback(
102                self.inner,
103                Box::into_raw(cb).cast(),
104                callback_deleter::<T>,
105                callback_entrypoint::<T>,
106            )
107        }
108    }
109
110    /// Register a new callback for analysis registration.
111    ///
112    /// These callbacks can be used to register custom analyses with the given
113    /// [ModuleAnalysisManager].
114    pub fn add_module_analysis_registration_callback<T>(&mut self, cb: T)
115    where
116        T: Fn(&mut ModuleAnalysisManager) + 'static,
117    {
118        let cb = Box::new(cb);
119
120        extern "C" fn callback_deleter<T>(cb: *const c_void) {
121            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
122        }
123
124        extern "C" fn callback_entrypoint<T>(cb: *const c_void, manager: *mut c_void)
125        where
126            T: Fn(&mut ModuleAnalysisManager) + 'static,
127        {
128            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
129            let mut manager = unsafe { ModuleAnalysisManager::from_raw(manager, None) };
130
131            cb(&mut manager);
132
133            Box::into_raw(cb);
134        }
135
136        unsafe {
137            super::passBuilderAddModuleAnalysisRegistrationCallback(
138                self.inner,
139                Box::into_raw(cb).cast(),
140                callback_deleter::<T>,
141                callback_entrypoint::<T>,
142            )
143        }
144    }
145
146    /// Register a new callback for analysis registration.
147    ///
148    /// These callbacks can be used to register custom analyses with the given
149    /// [FunctionAnalysisManager].
150    pub fn add_function_analysis_registration_callback<T>(&mut self, cb: T)
151    where
152        T: Fn(&mut FunctionAnalysisManager) + 'static,
153    {
154        let cb = Box::new(cb);
155
156        extern "C" fn callback_deleter<T>(cb: *const c_void) {
157            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
158        }
159
160        extern "C" fn callback_entrypoint<T>(cb: *const c_void, manager: *mut c_void)
161        where
162            T: Fn(&mut FunctionAnalysisManager) + 'static,
163        {
164            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
165            let mut manager = unsafe { FunctionAnalysisManager::from_raw(manager, None) };
166
167            cb(&mut manager);
168
169            Box::into_raw(cb);
170        }
171
172        unsafe {
173            super::passBuilderAddFunctionAnalysisRegistrationCallback(
174                self.inner,
175                Box::into_raw(cb).cast(),
176                callback_deleter::<T>,
177                callback_entrypoint::<T>,
178            )
179        }
180    }
181
182    /// Register a new callback to be triggered at the peephole
183    /// extension point.
184    ///
185    /// # From the LLVM documentation
186    ///
187    /// This extension point allows adding passes that perform peephole
188    /// optimizations similar to the instruction combiner.
189    ///
190    /// These passes will be inserted after each instance of the instruction
191    /// combiner pass.
192    pub fn add_peephole_ep_callback<T>(&mut self, cb: T)
193    where
194        T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
195    {
196        let cb = Box::new(cb);
197
198        extern "C" fn callback_deleter<T>(cb: *const c_void) {
199            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
200        }
201
202        extern "C" fn callback_entrypoint<T>(
203            cb: *const c_void,
204            manager: *mut c_void,
205            opt: OptimizationLevel,
206        ) where
207            T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
208        {
209            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
210            let mut manager = unsafe { FunctionPassManager::from_raw(manager) };
211
212            cb(&mut manager, opt);
213
214            Box::into_raw(cb);
215        }
216
217        unsafe {
218            super::passBuilderAddPeepholeEPCallback(
219                self.inner,
220                Box::into_raw(cb).cast(),
221                callback_deleter::<T>,
222                callback_entrypoint::<T>,
223            )
224        }
225    }
226
227    /// Register a new callback to be triggered at the optimizer
228    /// late extension point.
229    ///
230    /// # From the LLVM documentation
231    ///
232    /// This extension point allows adding optimization passes after
233    /// most of the main optimizations, but before the last cleanup-ish
234    /// optimizations.
235    pub fn add_scalar_optimizer_late_ep_callback<T>(&mut self, cb: T)
236    where
237        T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
238    {
239        let cb = Box::new(cb);
240
241        extern "C" fn callback_deleter<T>(cb: *const c_void) {
242            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
243        }
244
245        extern "C" fn callback_entrypoint<T>(
246            cb: *const c_void,
247            manager: *mut c_void,
248            opt: OptimizationLevel,
249        ) where
250            T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
251        {
252            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
253            let mut manager = unsafe { FunctionPassManager::from_raw(manager) };
254
255            cb(&mut manager, opt);
256
257            Box::into_raw(cb);
258        }
259
260        unsafe {
261            super::passBuilderAddScalarOptimizerLateEPCallback(
262                self.inner,
263                Box::into_raw(cb).cast(),
264                callback_deleter::<T>,
265                callback_entrypoint::<T>,
266            )
267        }
268    }
269
270    /// Register a new callback to be triggered at the vectorizer
271    /// start extension point.
272    ///
273    /// # From the LLVM documentation
274    ///
275    /// This extension point allows adding optimization passes before
276    /// the vectorizer and other highly target specific optimization
277    /// passes are executed.
278    pub fn add_vectorizer_start_ep_callback<T>(&mut self, cb: T)
279    where
280        T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
281    {
282        let cb = Box::new(cb);
283
284        extern "C" fn callback_deleter<T>(cb: *const c_void) {
285            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
286        }
287
288        extern "C" fn callback_entrypoint<T>(
289            cb: *const c_void,
290            manager: *mut c_void,
291            opt: OptimizationLevel,
292        ) where
293            T: Fn(&mut FunctionPassManager, OptimizationLevel) + 'static,
294        {
295            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
296            let mut manager = unsafe { FunctionPassManager::from_raw(manager) };
297
298            cb(&mut manager, opt);
299
300            Box::into_raw(cb);
301        }
302
303        unsafe {
304            super::passBuilderAddVectorizerStartEPCallback(
305                self.inner,
306                Box::into_raw(cb).cast(),
307                callback_deleter::<T>,
308                callback_entrypoint::<T>,
309            )
310        }
311    }
312
313    /// Register a new callback to be triggered at the pipeline
314    /// start extension point.
315    ///
316    /// # From the LLVM documentation
317    ///
318    /// This extension point allows adding optimization once at the start
319    /// of the pipeline. This does not apply to 'backend' compiles (LTO and
320    /// ThinLTO link-time pipelines).
321    #[llvm_versions(12..)]
322    pub fn add_pipeline_start_ep_callback<T>(&mut self, cb: T)
323    where
324        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
325    {
326        let cb = Box::new(cb);
327
328        extern "C" fn callback_deleter<T>(cb: *const c_void) {
329            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
330        }
331
332        extern "C" fn callback_entrypoint<T>(
333            cb: *const c_void,
334            manager: *mut c_void,
335            opt: OptimizationLevel,
336        ) where
337            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
338        {
339            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
340            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
341
342            cb(&mut manager, opt);
343
344            Box::into_raw(cb);
345        }
346
347        unsafe {
348            super::passBuilderAddPipelineStartEPCallback(
349                self.inner,
350                Box::into_raw(cb).cast(),
351                callback_deleter::<T>,
352                callback_entrypoint::<T>,
353            )
354        }
355    }
356
357    /// Register a new callback to be triggered at the pipeline
358    /// early simplification extension point.
359    ///
360    /// # From the LLVM documentation
361    ///
362    /// This extension point allows adding optimization right after passes
363    /// that do basic simplification of the input IR.
364    #[llvm_versions(12..)]
365    pub fn add_pipeline_early_simplification_ep_callback<T>(&mut self, cb: T)
366    where
367        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
368    {
369        let cb = Box::new(cb);
370
371        extern "C" fn callback_deleter<T>(cb: *const c_void) {
372            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
373        }
374
375        extern "C" fn callback_entrypoint<T>(
376            cb: *const c_void,
377            manager: *mut c_void,
378            opt: OptimizationLevel,
379        ) where
380            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
381        {
382            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
383            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
384
385            cb(&mut manager, opt);
386
387            Box::into_raw(cb);
388        }
389
390        unsafe {
391            super::passBuilderAddPipelineEarlySimplificationEPCallback(
392                self.inner,
393                Box::into_raw(cb).cast(),
394                callback_deleter::<T>,
395                callback_entrypoint::<T>,
396            )
397        }
398    }
399
400    /// Register a new callback to be triggered at the optimizer
401    /// last extension point.
402    ///
403    /// # From the LLVM documentation
404    ///
405    /// This extension point allows adding passes that run after everything
406    /// else.
407    #[llvm_versions(11..)]
408    pub fn add_optimizer_last_ep_callback<T>(&mut self, cb: T)
409    where
410        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
411    {
412        let cb = Box::new(cb);
413
414        extern "C" fn callback_deleter<T>(cb: *const c_void) {
415            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
416        }
417
418        extern "C" fn callback_entrypoint<T>(
419            cb: *const c_void,
420            manager: *mut c_void,
421            opt: OptimizationLevel,
422        ) where
423            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
424        {
425            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
426            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
427
428            cb(&mut manager, opt);
429
430            Box::into_raw(cb);
431        }
432
433        unsafe {
434            super::passBuilderAddOptimizerLastEPCallback(
435                self.inner,
436                Box::into_raw(cb).cast(),
437                callback_deleter::<T>,
438                callback_entrypoint::<T>,
439            )
440        }
441    }
442
443    /// Register a new callback to be triggered at the full LTO
444    /// early extension point.
445    ///
446    /// # From the LLVM documentation
447    ///
448    /// This extension point allow adding passes that run at Link Time,
449    /// before Full Link Time Optimization.
450    #[llvm_versions(15..)]
451    pub fn add_full_lto_early_ep_callback<T>(&mut self, cb: T)
452    where
453        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
454    {
455        let cb = Box::new(cb);
456
457        extern "C" fn callback_deleter<T>(cb: *const c_void) {
458            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
459        }
460
461        extern "C" fn callback_entrypoint<T>(
462            cb: *const c_void,
463            manager: *mut c_void,
464            opt: OptimizationLevel,
465        ) where
466            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
467        {
468            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
469            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
470
471            cb(&mut manager, opt);
472
473            Box::into_raw(cb);
474        }
475
476        unsafe {
477            super::passBuilderAddFullLinkTimeOptimizationEarlyEPCallback(
478                self.inner,
479                Box::into_raw(cb).cast(),
480                callback_deleter::<T>,
481                callback_entrypoint::<T>,
482            )
483        }
484    }
485
486    /// Register a new callback to be triggered at the full LTO
487    /// last extension point.
488    ///
489    /// # From the LLVM documentation
490    ///
491    /// This extensions point allow adding passes that run at Link Time,
492    /// after Full Link Time Optimization.
493    #[llvm_versions(15..)]
494    pub fn add_full_lto_last_ep_callback<T>(&mut self, cb: T)
495    where
496        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
497    {
498        let cb = Box::new(cb);
499
500        extern "C" fn callback_deleter<T>(cb: *const c_void) {
501            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
502        }
503
504        extern "C" fn callback_entrypoint<T>(
505            cb: *const c_void,
506            manager: *mut c_void,
507            opt: OptimizationLevel,
508        ) where
509            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
510        {
511            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
512            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
513
514            cb(&mut manager, opt);
515
516            Box::into_raw(cb);
517        }
518
519        unsafe {
520            super::passBuilderAddFullLinkTimeOptimizationLastEPCallback(
521                self.inner,
522                Box::into_raw(cb).cast(),
523                callback_deleter::<T>,
524                callback_entrypoint::<T>,
525            )
526        }
527    }
528
529    /// Register a new callback to be triggered at the optimizer
530    /// early extension point.
531    ///
532    /// # From the LLVM documentation
533    ///
534    /// This extension point allows adding passes just before the main
535    /// module-level optimization passes.
536    #[llvm_versions(15..)]
537    pub fn add_optimizer_early_ep_callback<T>(&mut self, cb: T)
538    where
539        T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
540    {
541        let cb = Box::new(cb);
542
543        extern "C" fn callback_deleter<T>(cb: *const c_void) {
544            drop(unsafe { Box::<T>::from_raw(cb as *mut _) })
545        }
546
547        extern "C" fn callback_entrypoint<T>(
548            cb: *const c_void,
549            manager: *mut c_void,
550            opt: OptimizationLevel,
551        ) where
552            T: Fn(&mut ModulePassManager, OptimizationLevel) + 'static,
553        {
554            let cb = unsafe { Box::<T>::from_raw(cb as *mut _) };
555            let mut manager = unsafe { ModulePassManager::from_raw(manager) };
556
557            cb(&mut manager, opt);
558
559            Box::into_raw(cb);
560        }
561
562        unsafe {
563            super::passBuilderAddOptimizerEarlyEPCallback(
564                self.inner,
565                Box::into_raw(cb).cast(),
566                callback_deleter::<T>,
567                callback_entrypoint::<T>,
568            )
569        }
570    }
571}
572
573/// Enum describing whether a pipeline parsing callback
574/// successfully parsed its given pipeline element.
575#[derive(Clone, Copy)]
576pub enum PipelineParsing {
577    /// The pipeline element was successfully parsed.
578    Parsed,
579
580    /// The pipeline element wasn't parsed.
581    NotParsed,
582}
583
584/// Enum for the LLVM-provided high-level optimization levels.
585///
586/// Each level has a specific goal and rationale.
587#[repr(C)]
588#[derive(Clone, Copy, Debug)]
589pub enum OptimizationLevel {
590    /// This level disables as many optimizations as possible.
591    O0,
592
593    /// This level optimizes quickly without destroying debuggability.
594    O1,
595
596    /// This level optimizes for fast execution as much as possible
597    /// without triggering significant incremental compile time or
598    /// code size growth.
599    O2,
600
601    /// This level optimizes for fast execution as much as possible.
602    O3,
603
604    /// This level is similar to **O2** but tries to optimize
605    /// for small code size instead of fast execution without
606    /// triggering significant incremental execution time slowdowns.
607    Os,
608
609    /// This level  will optimize for code size at any and all costs.
610    Oz,
611}