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}