mir/
codegen.rs

1use std::ffi::c_void;
2use std::fmt;
3
4use crate::{FuncItemRef, ImportResolver, MirContext, ffi};
5
6/// The MIR context for native code generation.
7pub struct MirGenContext {
8    ctx: MirContext,
9    #[cfg(feature = "gen-debug")]
10    debug_file: std::cell::Cell<Option<crate::mem_file::MemoryFile>>,
11}
12
13impl fmt::Debug for MirGenContext {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        f.debug_struct("MirGenContext")
16            .field("ctx", &self.ctx)
17            .finish_non_exhaustive()
18    }
19}
20
21impl MirGenContext {
22    /// Initialize the codegen context on top of an existing MIR context.
23    pub fn new(ctx: MirContext) -> Self {
24        unsafe { ffi::MIR_gen_init(ctx.ctx.as_ptr()) };
25        Self {
26            ctx,
27            #[cfg(feature = "gen-debug")]
28            debug_file: std::cell::Cell::new(None),
29        }
30    }
31
32    /// Set the optimization level to use.
33    ///
34    /// See details in [MIR documentation][upstream-docs].
35    ///
36    /// [upstream-docs]: https://github.com/vnmakarov/mir/blob/v1.0.0/MIR.md#user-content-mir-generator-file-mir-genh
37    pub fn set_opt_level(&self, level: u32) {
38        unsafe { ffi::MIR_gen_set_optimize_level(self.ctx.ctx.as_ptr(), level) };
39    }
40
41    /// Enable internal debug logging with specific level.
42    ///
43    /// Logs will be collected in memory and can be retrieved by [`Self::get_debug_output`].
44    #[cfg(feature = "gen-debug")]
45    pub fn enable_debug(&self, level: libc::c_int) {
46        let file = crate::mem_file::MemoryFile::new();
47        unsafe {
48            ffi::MIR_gen_set_debug_level(self.ctx.ctx.as_ptr(), level);
49            ffi::MIR_gen_set_debug_file(self.ctx.ctx.as_ptr(), file.file());
50        }
51        self.debug_file.set(Some(file));
52    }
53
54    /// Retrieve the debug logs collected so far.
55    ///
56    /// Note: logs will not be cleared after the call. Memory can only be freed after destroyed the
57    /// context.
58    ///
59    /// # Panics
60    ///
61    /// Panics if debug logging is not enabled before.
62    #[cfg(feature = "gen-debug")]
63    pub fn get_debug_output(&self) -> String {
64        let file = self.debug_file.take().expect("debug is not enabled");
65        let s = file.get_data_string();
66        self.debug_file.set(Some(file));
67        s
68    }
69
70    /// Link loaded modules and external names, preparing to be codegen.
71    ///
72    /// # Panics
73    ///
74    /// Panic from C on unresolved names.
75    pub fn link_modules_for_codegen(&self) {
76        unsafe { self.link_modules_raw(Some(ffi::MIR_set_gen_interface), None) }
77    }
78
79    /// Link loaded modules and external names with custom resolver, preparing to be codegen.
80    ///
81    /// # Safety
82    ///
83    /// `resolver` must return valid function pointers with prototype expected by generated code,
84    /// or `NULL` if unresolved.
85    ///
86    /// # Panics
87    ///
88    /// Panic from C on unresolved names.
89    pub unsafe fn link_modules_for_codegen_with_resolver(&self, resolver: &ImportResolver) {
90        unsafe { self.link_modules_raw(Some(ffi::MIR_set_gen_interface), Some(resolver)) }
91    }
92
93    /// Generate native code and return the function pointer to `func`.
94    ///
95    /// This function is idempotic, that is, once code is generated for `func`, all later calls
96    /// will do nothing and simply return the same function pointer.
97    pub fn codegen_func(&self, func: FuncItemRef<'_>) -> *mut c_void {
98        unsafe { ffi::MIR_gen(self.ctx.ctx.as_ptr(), func.as_raw()) }
99    }
100}
101
102impl Drop for MirGenContext {
103    fn drop(&mut self) {
104        #[cfg(feature = "gen-debug")]
105        {
106            if self.debug_file.get_mut().is_some() {
107                unsafe {
108                    ffi::MIR_gen_set_debug_file(self.ctx.ctx.as_ptr(), std::ptr::null_mut());
109                }
110            }
111        }
112        unsafe { ffi::MIR_gen_finish(self.ctx.ctx.as_ptr()) };
113    }
114}
115
116impl std::ops::Deref for MirGenContext {
117    type Target = MirContext;
118
119    fn deref(&self) -> &Self::Target {
120        &self.ctx
121    }
122}