mir/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2//! # [MIR project][mir] bindings for Rust
3//!
4//! [mir]: https://github.com/vnmakarov/mir
5//!
6//! ## Quick start
7//!
8//! > You can also check [`tests/smoke.rs`][more-example] for more examples.
9//!
10//! [more-example]: https://github.com/oxalica/mir-rs/blob/main/tests/smoke.rs
11//!
12//! ### Construct a function and execute it via MIR interpreter
13//!
14//! ```
15//! # #[cfg(feature = "interp")] {
16//! use mir::{InsnBuilder, MirContext, Ty, Val};
17//!
18//! // Initialize a context.
19//! let ctx = MirContext::new();
20//!
21//! // Create a module, a function, append instructions, and finally sealing them.
22//! let m = ctx.enter_new_module(c"module_add");
23//! // Note that MIR requires all argument types to be I64.
24//! // extern "C" fn add(a: i64, b: i64) -> i64
25//! let f = m.enter_new_function(c"add", &[Ty::I64], &[(c"a", Ty::I64), (c"b", Ty::I64)]);
26//! let a = f.get_reg(c"a");
27//! let b = f.get_reg(c"b");
28//! let ret = f.new_local_reg(c"ret", Ty::I64);
29//! f.ins().add(ret, a, b);
30//! f.ins().ret(ret);
31//! let func = f.finish();
32//! let module = m.finish();
33//!
34//! // Load and link modules.
35//! ctx.load_module(module);
36//! ctx.link_modules_for_interpret();
37//!
38//! // Execute our functions.
39//! let mut ret = [Val::default()];
40//! unsafe { ctx.interpret_unchecked(func, &mut ret, &[Val::from(40i64), Val::from(2i64)]) };
41//! assert_eq!(ret[0].as_i64(), 42);
42//! # }
43//! ```
44//!
45//! ### Codegen a function to native code and execute it natively
46//!
47//! ```
48//! # #[cfg(feature = "gen")] {
49//! use mir::{InsnBuilder, MirContext, MirGenContext, Ty};
50//!
51//! // Initialize a context and codegen context.
52//! let ctx = MirGenContext::new(MirContext::new());
53//!
54//! // Same creation code.
55//! let m = ctx.enter_new_module(c"module_add");
56//! // ...
57//! # let f = m.enter_new_function(c"add", &[Ty::I64], &[(c"a", Ty::I64), (c"b", Ty::I64)]);
58//! # let a = f.get_reg(c"a");
59//! # let b = f.get_reg(c"b");
60//! # let ret = f.new_local_reg(c"ret", Ty::I64);
61//! # f.ins().add(ret, a, b);
62//! # f.ins().ret(ret);
63//! # let func = f.finish();
64//! let module = m.finish();
65//!
66//! // Set optimization level and/or other configurables.
67//! ctx.set_opt_level(3);
68//! // Load and link modules, for codegen.
69//! ctx.load_module(module);
70//! ctx.link_modules_for_codegen();
71//!
72//! // Codegen and get a pointer to generated function.
73//! let func_ptr = ctx.codegen_func(func);
74//! type AddFunc = extern "C" fn(a: i64, b: i64) -> i64;
75//! let func_ptr = unsafe { std::mem::transmute::<*mut _, AddFunc>(func_ptr) };
76//!
77//! // Call it!
78//! assert_eq!(func_ptr(40, 2), 42);
79//! # }
80//! ```
81//!
82//! ## Panics and errors
83//!
84//! Unfortunately MIR [treats all errors as fatal errors][mir-error-issue] and is likely to
85//! continue be like this.
86//! In mir-rs, we did a best-effort recovery to unwind in error callback inside C code via
87//! ["C-unwind" ABI][c-unwind]. This is always safe because all intermediate C frames are
88//! [Plain Old Frames][pof]. But it may leave the C states inconsistent, thus it is strongly
89//! recommended to drop the [`MirContext`] if any modification method panics.
90//!
91//! [c-unwind]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
92//! [pof]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#plain-old-frames
93//! [mir-error-issue]: https://github.com/vnmakarov/mir/issues/220
94//!
95//! ## Features
96//!
97//! - `default`: Implies `io`, `interp`, `gen`.
98//!
99//! - `io`: De/serialization of MIR memory representation into/from bytes.
100//!
101//! - `interp`: Enables MIR interpreter.
102//!
103//! - `gen`: MIR native code generator.
104//!
105//! - `gen-debug`: Debug logging in MIR native code generator. It implies `gen`.
106#![warn(missing_debug_implementations)]
107#![warn(missing_docs)]
108use std::cell::Cell;
109use std::ffi::{CStr, c_char, c_void};
110use std::marker::PhantomData;
111use std::ptr::{self, NonNull, null, null_mut};
112
113use mem_file::MemoryFile;
114use types::{ErrorType, InsnBuilderBase};
115
116pub use mir_sys as ffi;
117pub use types::{
118    BssItemRef, DataItemRef, ExportItemRef, ExprDataItemRef, ForwardItemRef, FuncItemRef,
119    ImportItemRef, InsnBuilder, IntoOperand, IntoOutOperand, ItemRef, ItemType, Label,
120    LabelRefDataItemRef, MemOp, Operand, ProtoItemRef, RefDataItemRef, Reg, Ty,
121};
122
123#[cfg(feature = "gen")]
124mod codegen;
125#[cfg(feature = "gen")]
126pub use codegen::MirGenContext;
127
128#[cfg(feature = "interp")]
129mod interp;
130#[cfg(feature = "interp")]
131pub use interp::Val;
132
133mod mem_file;
134mod types;
135
136#[cfg(any(test, doctest))]
137mod tests;
138
139/// The context for code generation, linking and interpreter.
140///
141/// Almost all MIR functionality requires an initialized context to work.
142/// The context is not thread-safe.
143#[derive(Debug)]
144pub struct MirContext {
145    ctx: NonNull<ffi::MIR_context>,
146    in_module: Cell<bool>,
147    in_func: Cell<bool>,
148}
149
150impl Default for MirContext {
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156unsafe extern "C-unwind" {
157    fn MIRRS_error_handler_trampoline(
158        error_type: ffi::MIR_error_type_t,
159        format: *const c_char,
160        ...
161    ) -> !;
162}
163
164#[unsafe(no_mangle)]
165unsafe extern "C-unwind" fn MIRRS_error_handler_rust(
166    error_type: ffi::MIR_error_type_t,
167    msg: *const u8,
168    len: usize,
169) -> ! {
170    let msg = String::from_utf8_lossy(unsafe { std::slice::from_raw_parts(msg, len) });
171    panic!("mir error ({}): {}", ErrorType(error_type), msg);
172}
173
174#[cfg(feature = "io")]
175unsafe extern "C-unwind" fn write_byte_callback(data: *mut libc::c_void, byte: u8) -> libc::c_int {
176    let data = unsafe { &mut *data.cast::<Vec<_>>() };
177    data.push(byte);
178    1
179}
180
181#[cfg(feature = "io")]
182unsafe extern "C-unwind" fn read_byte_callback(data: *mut libc::c_void) -> libc::c_int {
183    let data = unsafe { &mut *data.cast::<&[u8]>() };
184    match data.split_first() {
185        Some((byte, rest)) => {
186            *data = rest;
187            (*byte).into()
188        }
189        None => libc::EOF,
190    }
191}
192
193type ImportResolver = dyn Fn(&CStr) -> *mut c_void;
194
195impl MirContext {
196    /// Initialize a new MIR context.
197    #[expect(clippy::missing_panics_doc, reason = "assertion")]
198    pub fn new() -> Self {
199        let ctx = ffi::MIR_init();
200        unsafe { ffi::MIR_set_error_func(ctx, Some(MIRRS_error_handler_trampoline)) };
201        Self {
202            ctx: NonNull::new(ctx).expect("context must not be NULL"),
203            in_module: Cell::new(false),
204            in_func: Cell::new(false),
205        }
206    }
207
208    /// Get the underlying pointer for FFI.
209    pub fn as_raw(&self) -> *mut ffi::MIR_context {
210        self.ctx.as_ptr()
211    }
212
213    /// Dump the content in a textual representation for human consumption.
214    #[must_use]
215    pub fn dump(&self) -> String {
216        MemoryFile::with(|file| unsafe { ffi::MIR_output(self.as_raw(), file) }).1
217    }
218
219    /// Get the list of all modules in the context.
220    ///
221    /// This includes all modules created in this context, not necessarily loaded or linked.
222    pub fn get_modules(&self) -> Vec<MirModuleRef<'_>> {
223        let head = unsafe { (*ffi::MIR_get_module_list(self.as_raw())).head };
224        std::iter::successors(NonNull::new(head), |m| unsafe {
225            NonNull::new(m.as_ref().module_link.next)
226        })
227        .map(|module| unsafe { MirModuleRef::from_raw(module.as_ptr()) })
228        .collect()
229    }
230
231    /// Serialize the context (including all modules) into bytes.
232    ///
233    /// The serialization format is stable across executions, but may not be stable across
234    /// MIR versions.
235    #[cfg(feature = "io")]
236    pub fn serialize(&self) -> Vec<u8> {
237        let mut buf = Vec::new();
238        unsafe {
239            ffi::MIR_write_with_func(
240                self.as_raw(),
241                Some(write_byte_callback),
242                ptr::from_mut(&mut buf).cast(),
243            );
244        }
245        buf
246    }
247
248    /// Deserialize context or modules from bytes.
249    ///
250    /// It will create one or more modules in `bytes`. The deserialized modules will be as if they
251    /// are created manually, not loaded or linked yet.
252    ///
253    /// # Safety
254    /// `bytes` must be trusted and produced from previous serialization.
255    ///
256    /// # Panics
257    ///
258    /// Panic if there is any unfinished module.
259    /// Panic from C if the `bytes` cannot be parsed or contain errors.
260    #[cfg(feature = "io")]
261    pub unsafe fn deserialize(&self, bytes: &[u8]) {
262        assert!(
263            !self.in_module.get(),
264            "must not have unfinished module on deserialization",
265        );
266
267        let mut bytes = bytes;
268        unsafe {
269            ffi::MIR_read_with_func(
270                self.as_raw(),
271                Some(read_byte_callback),
272                ptr::from_mut(&mut bytes).cast(),
273            );
274        }
275    }
276
277    /// Create a new module and enter it.
278    ///
279    /// The MIR context is stateful. When entering a new module, it should be correctly finished
280    /// before creating another module. See [`MirModuleBuilder`] for more details.
281    ///
282    /// # Panics
283    ///
284    /// Panic if there is any unfinished module.
285    pub fn enter_new_module(&self, name: &CStr) -> MirModuleBuilder<'_> {
286        assert!(!self.in_module.get(), "already inside a module");
287        let module =
288            unsafe { MirModuleRef::from_raw(ffi::MIR_new_module(self.as_raw(), name.as_ptr())) };
289        self.in_module.set(true);
290        MirModuleBuilder { module, ctx: self }
291    }
292
293    /// Load an MIR module for linking.
294    pub fn load_module(&self, module: MirModuleRef<'_>) {
295        unsafe { ffi::MIR_load_module(self.as_raw(), module.module.as_ptr()) };
296    }
297
298    /// Load an external name pointing to `addr` for linking.
299    ///
300    /// # Example
301    /// ```
302    /// # fn link(ctx: &mir::MirContext) {
303    /// // let ctx: &MirContext;
304    /// unsafe { ctx.load_external(c"memset", libc::memset as _) };
305    /// # }
306    /// ```
307    ///
308    /// # Safety
309    ///
310    /// `addr` must be a valid function pointer with prototype expected by generated code.
311    pub unsafe fn load_external(&self, name: &CStr, addr: *mut c_void) {
312        unsafe { ffi::MIR_load_external(self.as_raw(), name.as_ptr(), addr) };
313    }
314
315    /// Link loaded modules and external names with given custom interface and resolver.
316    ///
317    /// # Safety
318    ///
319    /// `set_interface` should be one of `MIR_set_*_interface`.
320    /// `resolver` must return valid function pointers with prototype expected by generated code.
321    pub unsafe fn link_modules_raw(
322        &self,
323        set_interface: Option<
324            unsafe extern "C-unwind" fn(ctx: ffi::MIR_context_t, item: ffi::MIR_item_t),
325        >,
326        resolver: Option<&ImportResolver>,
327    ) {
328        unsafe extern "C-unwind" fn trampoline(
329            data: *mut c_void,
330            name: *const c_char,
331        ) -> *mut c_void {
332            let name = unsafe { CStr::from_ptr(name) };
333            unsafe { (*data.cast::<&ImportResolver>())(name) }
334        }
335
336        // NB. Keep `resolver` alive on stack by taking a reference.
337        let (resolver, arg) = match &resolver {
338            Some(resolver) => (
339                Some(trampoline as _),
340                // NB. This is a pointer to fat reference to dyn Fn.
341                ptr::from_ref(resolver).cast_mut().cast(),
342            ),
343            None => (None, null_mut()),
344        };
345        unsafe { ffi::MIR_link(self.as_raw(), set_interface, resolver, arg) }
346    }
347}
348
349impl Drop for MirContext {
350    fn drop(&mut self) {
351        if std::thread::panicking() {
352            // MIR_finish* may fail. Avoid double-panicking when something already goes wrong.
353            return;
354        }
355
356        if self.in_func.get() {
357            unsafe { ffi::MIR_finish_func(self.as_raw()) };
358        }
359        if self.in_module.get() {
360            unsafe { ffi::MIR_finish_module(self.as_raw()) };
361        }
362        unsafe { ffi::MIR_finish(self.as_raw()) };
363    }
364}
365
366/// A module reference.
367#[derive(Debug, Clone, Copy)]
368pub struct MirModuleRef<'ctx> {
369    module: NonNull<ffi::MIR_module>,
370    _marker: PhantomData<&'ctx MirContext>,
371}
372
373impl MirModuleRef<'_> {
374    unsafe fn from_raw(raw: *mut ffi::MIR_module) -> Self {
375        Self {
376            module: NonNull::new(raw).expect("module must not be null"),
377            _marker: PhantomData,
378        }
379    }
380
381    /// Get the underlying pointer for FFI.
382    #[must_use]
383    pub fn as_raw(&self) -> *mut ffi::MIR_module {
384        self.module.as_ptr()
385    }
386
387    /// Get the name of the module.
388    #[must_use]
389    pub fn name(&self) -> &CStr {
390        unsafe { CStr::from_ptr(self.module.as_ref().name) }
391    }
392
393    /// Get the list of all items inside the module.
394    #[must_use]
395    pub fn get_items(&self) -> Vec<ItemRef<'_>> {
396        let head = unsafe { self.module.as_ref().items.head };
397        std::iter::successors(NonNull::new(head), |item| unsafe {
398            NonNull::new(item.as_ref().item_link.next)
399        })
400        .map(|item| unsafe { ItemRef::from_raw(item.as_ptr()) })
401        .collect()
402    }
403
404    /// Dump the content in a textual representation for human consumption.
405    #[must_use]
406    pub fn dump(&self, ctx: &MirContext) -> String {
407        MemoryFile::with(|file| unsafe {
408            ffi::MIR_output_module(ctx.as_raw(), file, self.as_raw());
409        })
410        .1
411    }
412
413    /// Serialize the module  into bytes.
414    ///
415    /// The serialization format is stable across executions, but may not be stable across
416    /// MIR versions.
417    #[cfg(feature = "io")]
418    pub fn serialize(&self, ctx: &MirContext) -> Vec<u8> {
419        let mut buf = Vec::new();
420        unsafe {
421            ffi::MIR_write_module_with_func(
422                ctx.as_raw(),
423                Some(write_byte_callback),
424                self.as_raw(),
425                ptr::from_mut(&mut buf).cast(),
426            );
427        }
428        buf
429    }
430}
431
432/// The currently building MIR module.
433///
434/// There can only be at most one unfinished module for each [`MirContext`].
435/// It is strongly advised to explicitly call [`MirModuleBuilder::finish`] to finish the current
436/// module for clarity and observe any error outside drop impl.
437/// [`MirModuleBuilder`] will automatically finish it on drop.
438#[derive(Debug)]
439#[must_use = "module builder should be correctly finished by finish()"]
440pub struct MirModuleBuilder<'ctx> {
441    module: MirModuleRef<'ctx>,
442    ctx: &'ctx MirContext,
443}
444
445impl Drop for MirModuleBuilder<'_> {
446    fn drop(&mut self) {
447        let prev = self.ctx.in_module.replace(false);
448        debug_assert!(prev, "must be inside a module");
449        unsafe { ffi::MIR_finish_module(self.as_raw_ctx()) };
450    }
451}
452
453impl<'ctx> MirModuleBuilder<'ctx> {
454    /// Explicitly finish the module and return the module reference.
455    ///
456    /// # Panics
457    ///
458    /// Panic from C if the module content is malformed.
459    #[expect(clippy::must_use_candidate, reason = "can be ignored")]
460    pub fn finish(self) -> MirModuleRef<'ctx> {
461        self.module
462        // Implicit `drop(self)`.
463    }
464
465    fn as_raw_ctx(&self) -> *mut ffi::MIR_context {
466        self.ctx.as_raw()
467    }
468
469    /// Add a new function prototype and return its reference.
470    ///
471    /// # Panics
472    ///
473    /// Panic from C on duplicated names or invalid signature.
474    #[must_use]
475    pub fn add_proto(&self, name: &CStr, rets: &[Ty], args: &[(&CStr, Ty)]) -> ProtoItemRef<'_> {
476        let c_args = args
477            .iter()
478            .map(|(name, ty)| ffi::MIR_var {
479                type_: ty.0,
480                name: name.as_ptr(),
481                // Unused.
482                size: 0,
483            })
484            .collect::<Vec<_>>();
485        let item = unsafe {
486            ItemRef::from_raw(ffi::MIR_new_proto_arr(
487                self.as_raw_ctx(),
488                name.as_ptr(),
489                rets.len(),
490                rets.as_ptr().cast::<ffi::MIR_type_t>().cast_mut(),
491                c_args.len(),
492                c_args.as_ptr().cast_mut(),
493            ))
494        };
495        ProtoItemRef(item)
496    }
497
498    /// Add a new import name and return its reference.
499    ///
500    /// # Panics
501    ///
502    /// Panic from C on duplicated names.
503    #[must_use]
504    pub fn add_import(&self, name: &CStr) -> ImportItemRef<'_> {
505        let item =
506            unsafe { ItemRef::from_raw(ffi::MIR_new_import(self.as_raw_ctx(), name.as_ptr())) };
507        ImportItemRef(item)
508    }
509
510    /// Add a new export name and return its reference.
511    ///
512    /// # Panics
513    ///
514    /// Panic from C on duplicated names.
515    #[expect(clippy::must_use_candidate, reason = "can be ignored")]
516    pub fn add_export(&self, name: &CStr) -> ExportItemRef<'_> {
517        let item =
518            unsafe { ItemRef::from_raw(ffi::MIR_new_export(self.as_raw_ctx(), name.as_ptr())) };
519        ExportItemRef(item)
520    }
521
522    /// Add a new forward declaration and return its reference.
523    ///
524    /// # Panics
525    ///
526    /// Panic from C on duplicated names.
527    #[expect(clippy::must_use_candidate, reason = "can be ignored")]
528    pub fn add_forward(&self, name: &CStr) -> ForwardItemRef<'_> {
529        let item =
530            unsafe { ItemRef::from_raw(ffi::MIR_new_forward(self.as_raw_ctx(), name.as_ptr())) };
531        ForwardItemRef(item)
532    }
533
534    /// Add a new forward declaration and return its reference.
535    ///
536    /// # Panics
537    ///
538    /// Panic from C on duplicated names.
539    #[must_use]
540    pub fn add_data<'a>(&self, name: impl Into<Option<&'a CStr>>, data: &[u8]) -> DataItemRef<'_> {
541        unsafe {
542            DataItemRef(ItemRef::from_raw(ffi::MIR_new_data(
543                self.as_raw_ctx(),
544                name.into().map_or(null(), CStr::as_ptr),
545                Ty::U8.0,
546                data.len(),
547                data.as_ptr().cast(),
548            )))
549        }
550    }
551
552    /// Add a new data referencing another item and return its reference.
553    ///
554    /// The address of `ref_item` after linking plus `disp` is used to initialize the data.
555    ///
556    /// # Panics
557    ///
558    /// Panic from C on duplicated names.
559    #[must_use]
560    pub fn add_ref_data<'a>(
561        &self,
562        name: impl Into<Option<&'a CStr>>,
563        ref_item: ItemRef<'_>,
564        disp: i64,
565    ) -> RefDataItemRef<'_> {
566        unsafe {
567            RefDataItemRef(ItemRef::from_raw(ffi::MIR_new_ref_data(
568                self.as_raw_ctx(),
569                name.into().map_or(null(), CStr::as_ptr),
570                ref_item.as_raw(),
571                disp,
572            )))
573        }
574    }
575
576    /// Add a new data initialized by a function and return its reference.
577    ///
578    /// - Not all MIR functions can be used for expression data. The expression function should
579    ///   have only one result, have no arguments, not use any call or any instruction with memory.
580    /// - The expression function is called during linking and its result is used to initialize the
581    ///   data.
582    ///
583    /// # Panics
584    ///
585    /// Panic from C on duplicated names or unsupported functions.
586    #[must_use]
587    pub fn add_expr_data<'a>(
588        &self,
589        name: impl Into<Option<&'a CStr>>,
590        expr_func: FuncItemRef<'_>,
591    ) -> ExprDataItemRef<'_> {
592        unsafe {
593            ExprDataItemRef(ItemRef::from_raw(ffi::MIR_new_expr_data(
594                self.as_raw_ctx(),
595                name.into().map_or(null(), CStr::as_ptr),
596                expr_func.as_raw(),
597            )))
598        }
599    }
600
601    /// Add a new data initialized by a label and return its reference.
602    ///
603    /// The addresses defined as `label[-base_label]+disp` where `base_label` can be `None`.
604    /// `lref` can refers for labels the same function (this is checked during module load) and
605    /// there is a warranty label addresses to be defined only at the beginning of the function
606    /// execution.
607    ///
608    /// # Panics
609    ///
610    /// Panic from C on duplicated names.
611    #[must_use]
612    pub fn add_label_ref_data<'module>(
613        &'module self,
614        name: &CStr,
615        label: Label<'module>,
616        base_label: Option<Label<'module>>,
617        disp: i64,
618    ) -> LabelRefDataItemRef<'module> {
619        unsafe {
620            LabelRefDataItemRef(ItemRef::from_raw(ffi::MIR_new_lref_data(
621                self.as_raw_ctx(),
622                name.as_ptr(),
623                label.0,
624                base_label.map_or(null_mut(), |lbl| lbl.0),
625                disp,
626            )))
627        }
628    }
629
630    /// Add a new writable memory segment and return its reference.
631    ///
632    /// # Panics
633    ///
634    /// Panic from C on duplicated names.
635    #[must_use]
636    pub fn add_bss<'a>(&self, name: impl Into<Option<&'a CStr>>, len: usize) -> BssItemRef<'_> {
637        unsafe {
638            BssItemRef(ItemRef::from_raw(ffi::MIR_new_bss(
639                self.as_raw_ctx(),
640                name.into().map_or(null(), CStr::as_ptr),
641                len,
642            )))
643        }
644    }
645
646    /// Add a new function definition and enter into it.
647    ///
648    /// The MIR context is stateful and the previous function must be finished before creating
649    /// another one.
650    /// See more details in [`MirFuncBuilder`].
651    ///
652    /// # Panics
653    ///
654    /// Panic if there is any unfinished function.
655    /// Panic from C on duplicated names or invalid signature.
656    #[must_use]
657    pub fn enter_new_function<'module>(
658        &'module self,
659        name: &CStr,
660        rets: &[Ty],
661        args: &[(&CStr, Ty)],
662    ) -> MirFuncBuilder<'module, 'ctx> {
663        assert!(!self.ctx.in_func.get(), "already inside a function");
664        let c_args = args
665            .iter()
666            .map(|(name, ty)| ffi::MIR_var {
667                type_: ty.0,
668                name: name.as_ptr(),
669                // Unused.
670                size: 0,
671            })
672            .collect::<Vec<_>>();
673        let func = unsafe {
674            ffi::MIR_new_func_arr(
675                self.as_raw_ctx(),
676                name.as_ptr(),
677                rets.len(),
678                rets.as_ptr().cast::<ffi::MIR_type_t>().cast_mut(),
679                c_args.len(),
680                c_args.as_ptr().cast_mut(),
681            )
682        };
683        self.ctx.in_func.set(true);
684        MirFuncBuilder {
685            func: unsafe { FuncItemRef(ItemRef::from_raw(func)) },
686            ctx: self.ctx,
687            _marker: PhantomData,
688        }
689    }
690}
691
692/// The currently building MIR function.
693///
694/// There can only be at most one unfinished function for each [`MirContext`] inside one unfinished
695/// module. It is strongly advised to explicitly call [`MirFuncBuilder::finish`] to finish the
696/// current function for clarity and observe any error outside drop impl.
697/// [`MirFuncBuilder`] will automatically finish it on drop.
698#[derive(Debug)]
699pub struct MirFuncBuilder<'module, 'ctx> {
700    func: FuncItemRef<'ctx>,
701    ctx: &'ctx MirContext,
702    _marker: PhantomData<&'module MirModuleRef<'ctx>>,
703}
704
705impl Drop for MirFuncBuilder<'_, '_> {
706    fn drop(&mut self) {
707        let prev = self.ctx.in_func.replace(false);
708        debug_assert!(prev, "must be inside a function");
709        unsafe { ffi::MIR_finish_func(self.ctx.ctx.as_ptr()) };
710    }
711}
712
713impl<'module, 'ctx> MirFuncBuilder<'module, 'ctx> {
714    /// Explicitly finish the function and return the function reference.
715    ///
716    /// # Panics
717    ///
718    /// Panic from C if the function content is malformed.
719    #[expect(clippy::must_use_candidate, reason = "can be ignored")]
720    pub fn finish(self) -> FuncItemRef<'ctx> {
721        self.func
722        // Implicit `drop(self)`.
723    }
724
725    /// Get the virtual register or given name.
726    ///
727    /// Function parameters are represented as pre-defined virtual registers of same names.
728    #[must_use]
729    pub fn get_reg(&self, name: &CStr) -> Reg {
730        let reg = unsafe {
731            ffi::MIR_reg(
732                self.ctx.ctx.as_ptr(),
733                name.as_ptr(),
734                ptr::from_ref(self.func.data()).cast_mut(),
735            )
736        };
737        Reg(reg)
738    }
739
740    /// Create a new virtual register.
741    #[must_use]
742    pub fn new_local_reg(&self, name: &CStr, ty: Ty) -> Reg {
743        let reg = unsafe {
744            ffi::MIR_new_func_reg(
745                self.ctx.ctx.as_ptr(),
746                ptr::from_ref(self.func.data()).cast_mut(),
747                ty.0,
748                name.as_ptr(),
749            )
750        };
751        Reg(reg)
752    }
753
754    /// Create a new unbound label.
755    ///
756    /// The label must be inserted later via [`InsnBuilder::label`].
757    #[must_use]
758    pub fn new_label(&self) -> Label<'module> {
759        let insn = unsafe { ffi::MIR_new_label(self.ctx.ctx.as_ptr()) };
760        Label(insn, PhantomData)
761    }
762
763    /// Append a new instruction to the function.
764    ///
765    /// See [`InsnBuilder`] for all instructions available.
766    #[must_use = "ins() does nothing unless chaining a method call"]
767    pub fn ins<'func>(&'func self) -> impl InsnBuilder<'module> + use<'func, 'module, 'ctx> {
768        FuncInsnBuilder { func_builder: self }
769    }
770}
771
772/// The instruction appender to an function.
773#[derive(Debug)]
774struct FuncInsnBuilder<'func, 'module, 'ctx> {
775    func_builder: &'func MirFuncBuilder<'module, 'ctx>,
776}
777
778unsafe impl<'module> InsnBuilderBase<'module> for FuncInsnBuilder<'_, 'module, '_> {
779    fn get_raw_ctx(&self) -> ffi::MIR_context_t {
780        self.func_builder.ctx.as_raw()
781    }
782
783    unsafe fn insert(self, insn: ffi::MIR_insn_t) {
784        unsafe { ffi::MIR_append_insn(self.get_raw_ctx(), self.func_builder.func.as_raw(), insn) };
785    }
786}