llvm_plugin/
analysis.rs

1use std::ffi::c_void;
2
3use inkwell::module::Module;
4use inkwell::values::{AsValueRef, FunctionValue};
5
6use crate::{LlvmFunctionAnalysis, LlvmModuleAnalysis};
7
8/// Struct allowing to query the pass manager for the result of
9/// analyses on function IR.
10pub struct FunctionAnalysisManager {
11    inner: *mut c_void,
12    from_analysis_id: Option<crate::AnalysisKey>,
13}
14
15impl FunctionAnalysisManager {
16    #[doc(hidden)]
17    pub unsafe fn from_raw(
18        inner: *mut c_void,
19        from_analysis_id: Option<crate::AnalysisKey>,
20    ) -> Self {
21        Self {
22            inner,
23            from_analysis_id,
24        }
25    }
26
27    /// Returns the result of the analysis on a given function IR.
28    ///
29    /// If the result is not in cache, the pass manager will execute the
30    /// analysis pass. Otherwise, the result is directly returned from cache.
31    ///
32    /// # Panics
33    ///
34    /// Panics if the given analysis wasn't registered, or if this function was
35    /// called within the given analysis itself.
36    pub fn get_result<A>(&self, function: &FunctionValue<'_>) -> &A::Result
37    where
38        A: crate::LlvmFunctionAnalysis,
39    {
40        let id = A::id();
41        assert!(
42            !matches!(self.from_analysis_id, Some(n) if id == n),
43            "Analysis cannot request its own result"
44        );
45
46        unsafe {
47            let res =
48                crate::get_function_analysis_result(self.inner, id, function.as_value_ref().cast());
49            Box::leak(Box::from_raw(res.cast()))
50        }
51    }
52
53    /// Returns the result of the analysis on a given function IR.
54    ///
55    /// If the result is not in cache, `None` is returned. Otherwise,
56    /// the result is directly returned from cache.
57    ///
58    /// This function never triggers the execution of an analysis.
59    ///
60    /// # Panics
61    ///
62    /// Panics if the given analysis wasn't registered, or if this function was
63    /// called within the given analysis itself.
64    pub fn get_cached_result<A>(&self, function: &FunctionValue<'_>) -> Option<&A::Result>
65    where
66        A: crate::LlvmFunctionAnalysis,
67    {
68        let id = A::id();
69        assert!(
70            !matches!(self.from_analysis_id, Some(n) if id == n),
71            "Analysis cannot request its own result"
72        );
73
74        let res = crate::get_function_analysis_cached_result(
75            self.inner,
76            id,
77            function.as_value_ref().cast(),
78        );
79
80        if !res.is_null() {
81            let res = unsafe { Box::leak(Box::from_raw(res.cast())) };
82            Some(res)
83        } else {
84            None
85        }
86    }
87
88    /// Register an analysis pass to the analysis manager.
89    ///
90    /// # Panics
91    ///
92    /// Panics if the given analysis was already registered.
93    pub fn register_pass<T>(&mut self, pass: T)
94    where
95        T: LlvmFunctionAnalysis,
96    {
97        let pass = Box::new(pass);
98
99        extern "C" fn result_deleter<T>(data: *mut c_void)
100        where
101            T: LlvmFunctionAnalysis,
102        {
103            drop(unsafe { Box::<<T as LlvmFunctionAnalysis>::Result>::from_raw(data.cast()) })
104        }
105
106        extern "C" fn pass_deleter<T>(pass: *mut c_void) {
107            drop(unsafe { Box::<T>::from_raw(pass.cast()) })
108        }
109
110        extern "C" fn pass_entrypoint<T>(
111            pass: *mut c_void,
112            function: *mut c_void,
113            manager: *mut c_void,
114            res: *mut *mut c_void,
115            res_deleter: *mut extern "C" fn(*mut c_void),
116        ) where
117            T: LlvmFunctionAnalysis,
118        {
119            let pass = unsafe { Box::<T>::from_raw(pass.cast()) };
120            let function = unsafe { FunctionValue::new(function.cast()).unwrap() };
121            let manager = unsafe { FunctionAnalysisManager::from_raw(manager, Some(T::id())) };
122
123            let data = pass.run_analysis(&function, &manager);
124
125            let data = Box::new(data);
126            unsafe {
127                *res = Box::<<T as LlvmFunctionAnalysis>::Result>::into_raw(data).cast();
128                *res_deleter = result_deleter::<T>;
129            }
130
131            Box::into_raw(pass);
132            #[allow(forgetting_copy_types)]
133            std::mem::forget(function);
134        }
135
136        let success = unsafe {
137            super::functionAnalysisManagerRegisterPass(
138                self.inner,
139                Box::into_raw(pass).cast(),
140                pass_deleter::<T>,
141                pass_entrypoint::<T>,
142                T::id(),
143            )
144        };
145
146        assert!(success, "analysis already registered");
147    }
148}
149
150/// Struct allowing to query the pass manager for the result of
151/// analyses on module IR.
152pub struct ModuleAnalysisManager {
153    inner: *mut c_void,
154    from_analysis_id: Option<crate::AnalysisKey>,
155}
156
157impl ModuleAnalysisManager {
158    #[doc(hidden)]
159    pub unsafe fn from_raw(
160        inner: *mut c_void,
161        from_analysis_id: Option<crate::AnalysisKey>,
162    ) -> Self {
163        Self {
164            inner,
165            from_analysis_id,
166        }
167    }
168
169    /// Returns the result of the analysis on a given module IR.
170    ///
171    /// If the result is not in cache, the pass manager will execute the
172    /// analysis pass. Otherwise, the result is directly returned from cache.
173    ///
174    /// # Panics
175    ///
176    /// Panics if the given analysis wasn't registered, or if this function was
177    /// called within the given analysis itself.
178    pub fn get_result<A>(&self, module: &Module<'_>) -> &A::Result
179    where
180        A: crate::LlvmModuleAnalysis,
181    {
182        let id = A::id();
183        assert!(
184            !matches!(self.from_analysis_id, Some(n) if id == n),
185            "Analysis cannot request its own result"
186        );
187
188        let res =
189            crate::get_module_analysis_result(self.inner, A::id(), module.as_mut_ptr().cast());
190
191        unsafe { Box::leak(Box::from_raw(res.cast())) }
192    }
193
194    /// Returns the result of the analysis on a given module IR.
195    ///
196    /// If the result is not in cache, `None` is returned. Otherwise,
197    /// the result is directly returned from cache.
198    ///
199    /// This function never triggers the execution of an analysis.
200    ///
201    /// # Panics
202    ///
203    /// Panics if the given analysis wasn't registered, or if this function was
204    /// called within the given analysis itself.
205    pub fn get_cached_result<A>(&self, module: &Module<'_>) -> Option<&A::Result>
206    where
207        A: crate::LlvmModuleAnalysis,
208    {
209        let id = A::id();
210        assert!(
211            !matches!(self.from_analysis_id, Some(n) if id == n),
212            "Analysis cannot request its own result"
213        );
214
215        let res = crate::get_module_analysis_cached_result(
216            self.inner,
217            A::id(),
218            module.as_mut_ptr().cast(),
219        );
220
221        if !res.is_null() {
222            let res = unsafe { Box::leak(Box::from_raw(res.cast())) };
223            Some(res)
224        } else {
225            None
226        }
227    }
228
229    /// Returns a [FunctionAnalysisManagerProxy], which is essentially an interface
230    /// allowing management of analyses at the function level.
231    pub fn get_function_analysis_manager_proxy(
232        &self,
233        module: &Module<'_>,
234    ) -> FunctionAnalysisManagerProxy {
235        let proxy = crate::get_function_analysis_manager_module_proxy(
236            self.inner,
237            module.as_mut_ptr().cast(),
238        );
239        FunctionAnalysisManagerProxy { inner: proxy }
240    }
241
242    /// Register an analysis pass to the analysis manager.
243    ///
244    /// # Panics
245    ///
246    /// Panics if the given analysis was already registered.
247    pub fn register_pass<T>(&mut self, pass: T)
248    where
249        T: LlvmModuleAnalysis,
250    {
251        let pass = Box::new(pass);
252
253        extern "C" fn result_deleter<T>(data: *mut c_void)
254        where
255            T: LlvmModuleAnalysis,
256        {
257            drop(unsafe { Box::<<T as LlvmModuleAnalysis>::Result>::from_raw(data.cast()) })
258        }
259
260        extern "C" fn pass_deleter<T>(pass: *mut c_void) {
261            drop(unsafe { Box::<T>::from_raw(pass.cast()) })
262        }
263
264        extern "C" fn pass_entrypoint<T>(
265            pass: *mut c_void,
266            module: *mut c_void,
267            manager: *mut c_void,
268            res: *mut *mut c_void,
269            res_deleter: *mut extern "C" fn(*mut c_void),
270        ) where
271            T: LlvmModuleAnalysis,
272        {
273            let pass = unsafe { Box::<T>::from_raw(pass.cast()) };
274            let module = unsafe { Module::new(module.cast()) };
275            let manager = unsafe { ModuleAnalysisManager::from_raw(manager, Some(T::id())) };
276
277            let data = pass.run_analysis(&module, &manager);
278
279            let data = Box::new(data);
280            unsafe {
281                *res = Box::<<T as LlvmModuleAnalysis>::Result>::into_raw(data).cast();
282                *res_deleter = result_deleter::<T>;
283            }
284
285            Box::into_raw(pass);
286            std::mem::forget(module);
287        }
288
289        let success = unsafe {
290            super::moduleAnalysisManagerRegisterPass(
291                self.inner,
292                Box::into_raw(pass).cast(),
293                pass_deleter::<T>,
294                pass_entrypoint::<T>,
295                T::id(),
296            )
297        };
298
299        assert!(success, "analysis already registered");
300    }
301}
302
303/// Struct allowing to make queries to the pass manager about function-level
304/// analyses.
305///
306/// The main use-case of such interface is to give the ability for module-level
307/// passes to trigger/query function-level analyses.
308///
309/// # Example
310///
311/// ```
312/// # use llvm_plugin::inkwell::module::Module;
313/// # use llvm_plugin::inkwell::values::FunctionValue;
314/// # use llvm_plugin::{
315/// #    AnalysisKey, FunctionAnalysisManager, LlvmFunctionAnalysis, LlvmModulePass,
316/// #    ModuleAnalysisManager, PreservedAnalyses,
317/// # };
318/// struct Pass;
319/// impl LlvmModulePass for Pass {
320///     fn run_pass(
321///         &self,
322///         module: &mut Module,
323///         manager: &ModuleAnalysisManager,
324///     ) -> PreservedAnalyses {
325///         let manager = manager
326///             .get_function_analysis_manager_proxy(&module)
327///             .get_manager();
328///
329///         let function = module.get_first_function().unwrap();
330///         let result = manager.get_result::<Analysis>(&function);
331///         assert_eq!(result, "Some result");
332///
333///         PreservedAnalyses::All
334///     }
335/// }
336///
337/// struct Analysis;
338/// impl LlvmFunctionAnalysis for Analysis {
339///     type Result = String;
340///
341///     fn run_analysis(
342///         &self,
343///         _function: &FunctionValue,
344///         _manager: &FunctionAnalysisManager,
345///     ) -> Self::Result {
346///         "Some result".to_owned()
347///     }
348///
349///     fn id() -> AnalysisKey {
350///         1 as AnalysisKey
351///     }
352/// }
353/// ```
354pub struct FunctionAnalysisManagerProxy {
355    inner: *mut c_void,
356}
357
358impl FunctionAnalysisManagerProxy {
359    /// Returns the inner [FunctionAnalysisManager].
360    pub fn get_manager(&self) -> FunctionAnalysisManager {
361        let manager = crate::get_function_analysis_manager(self.inner);
362        FunctionAnalysisManager {
363            inner: manager,
364            from_analysis_id: None,
365        }
366    }
367}