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::InsnBuilderBase;
115
116pub use mir_sys as ffi;
117pub use types::{
118 BssItemRef, DataItemRef, ExportItemRef, ExprDataItemRef, ForwardItemRef, FuncItemRef,
119 ImportItemRef, InsnBuilder, IntoOperand, IntoOutOperand, ItemRef, Label, LabelRefDataItemRef,
120 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/// The context for code generation, linking and interpreter.
137///
138/// Almost all MIR functionality requires an initialized context to work.
139/// The context is not thread-safe.
140#[derive(Debug)]
141pub struct MirContext {
142 ctx: NonNull<ffi::MIR_context>,
143 module: Cell<Option<NonNull<ffi::MIR_module>>>,
144 func_item: Cell<Option<NonNull<ffi::MIR_item>>>,
145}
146
147impl Default for MirContext {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153unsafe extern "C-unwind" {
154 fn MIRRS_error_handler_trampoline(
155 error_type: ffi::MIR_error_type_t,
156 format: *const c_char,
157 ...
158 ) -> !;
159}
160
161#[unsafe(no_mangle)]
162unsafe extern "C-unwind" fn MIRRS_error_handler_rust(
163 error_type: ffi::MIR_error_type_t,
164 msg: *const u8,
165 len: usize,
166) -> ! {
167 let msg = String::from_utf8_lossy(unsafe { std::slice::from_raw_parts(msg, len) });
168 panic!("mir error {error_type}: {msg}");
169}
170
171#[cfg(feature = "io")]
172unsafe extern "C-unwind" fn write_byte_callback(data: *mut libc::c_void, byte: u8) -> libc::c_int {
173 let data = unsafe { &mut *data.cast::<Vec<_>>() };
174 data.push(byte);
175 1
176}
177
178#[cfg(feature = "io")]
179unsafe extern "C-unwind" fn read_byte_callback(data: *mut libc::c_void) -> libc::c_int {
180 let data = unsafe { &mut *data.cast::<&[u8]>() };
181 match data.split_first() {
182 Some((byte, rest)) => {
183 *data = rest;
184 (*byte).into()
185 }
186 None => libc::EOF,
187 }
188}
189
190type ImportResolver = dyn Fn(&CStr) -> *mut c_void;
191
192impl MirContext {
193 /// Initialize a new MIR context.
194 #[expect(clippy::missing_panics_doc, reason = "assertion")]
195 pub fn new() -> Self {
196 let ctx = ffi::MIR_init();
197 unsafe { ffi::MIR_set_error_func(ctx, Some(MIRRS_error_handler_trampoline)) };
198 Self {
199 ctx: NonNull::new(ctx).expect("context must not be NULL"),
200 module: Cell::new(None),
201 func_item: Cell::new(None),
202 }
203 }
204
205 /// Get the underlying pointer for FFI.
206 pub fn as_raw(&self) -> *mut ffi::MIR_context {
207 self.ctx.as_ptr()
208 }
209
210 /// Dump the content of the context in a textual representation for human consumption.
211 #[must_use]
212 pub fn dump(&self) -> String {
213 MemoryFile::with(|file| unsafe { ffi::MIR_output(self.as_raw(), file) }).1
214 }
215
216 /// Get the list of all modules in the context.
217 ///
218 /// This includes all modules created in this context, not necessarily loaded or linked.
219 pub fn get_modules(&self) -> Vec<MirModuleRef<'_>> {
220 let head = unsafe { (*ffi::MIR_get_module_list(self.as_raw())).head };
221 std::iter::successors(NonNull::new(head), |m| unsafe {
222 NonNull::new(m.as_ref().module_link.next)
223 })
224 .map(|module| unsafe { MirModuleRef::from_raw(module.as_ptr()) })
225 .collect()
226 }
227
228 /// Serialize the context (including all modules) into bytes.
229 ///
230 /// The serialization format is stable across executions, but may not be stable across
231 /// MIR versions.
232 #[cfg(feature = "io")]
233 pub fn serialize(&self) -> Vec<u8> {
234 let mut buf = Vec::new();
235 unsafe {
236 ffi::MIR_write_with_func(
237 self.as_raw(),
238 Some(write_byte_callback),
239 ptr::from_mut(&mut buf).cast(),
240 );
241 }
242 buf
243 }
244
245 /// Deserialize context or modules from bytes.
246 ///
247 /// It will create one or more modules in `bytes`. The deserialized modules will be as ifthey
248 /// are created manually, not loaded or linked yet.
249 ///
250 /// # Safety
251 /// `bytes` must be trusted and produced from previous serialization.
252 ///
253 /// # Panics
254 ///
255 /// Panic if there is any unfinished module.
256 /// Panic from C if the `bytes` cannot be parsed or contain errors.
257 #[cfg(feature = "io")]
258 pub unsafe fn deserialize(&self, bytes: &[u8]) {
259 assert!(
260 self.module.get().is_none(),
261 "must not have unfinished module on deserialization",
262 );
263
264 let mut bytes = bytes;
265 unsafe {
266 ffi::MIR_read_with_func(
267 self.as_raw(),
268 Some(read_byte_callback),
269 ptr::from_mut(&mut bytes).cast(),
270 );
271 }
272 }
273
274 /// Create a new module and enter into it.
275 ///
276 /// The MIR context is stateful. When entering a new module, it should be correctly finished
277 /// before creating another module. See [`MirModuleBuilder`] for more details.
278 ///
279 /// # Panics
280 ///
281 /// Panic if there is any unfinished module.
282 pub fn enter_new_module(&self, name: &CStr) -> MirModuleBuilder<'_> {
283 assert!(self.module.get().is_none(), "already inside a module");
284 let module = unsafe { ffi::MIR_new_module(self.as_raw(), name.as_ptr()) };
285 self.module
286 .set(Some(NonNull::new(module).expect("module must not be null")));
287 MirModuleBuilder { ctx: self }
288 }
289
290 /// Load an MIR module for linking.
291 pub fn load_module(&self, module: MirModuleRef<'_>) {
292 unsafe { ffi::MIR_load_module(self.as_raw(), module.module.as_ptr()) };
293 }
294
295 /// Load an external name pointing to `addr` for linking.
296 ///
297 /// # Example
298 /// ```
299 /// # fn link(ctx: &mir::MirContext) {
300 /// // let ctx: &MirContext;
301 /// unsafe { ctx.load_external(c"memset", libc::memset as _) };
302 /// # }
303 /// ```
304 ///
305 /// # Safety
306 ///
307 /// `addr` must be a valid function pointer with prototype expected by generated code.
308 pub unsafe fn load_external(&self, name: &CStr, addr: *mut c_void) {
309 unsafe { ffi::MIR_load_external(self.as_raw(), name.as_ptr(), addr) };
310 }
311
312 /// Link loaded modules and external names with given custom interface and resolver.
313 ///
314 /// # Safety
315 ///
316 /// `set_interface` should be one of `MIR_set_*_interface`.
317 /// `resolver` must return valid function pointers with prototype expected by generated code.
318 pub unsafe fn link_modules_raw(
319 &self,
320 set_interface: Option<
321 unsafe extern "C-unwind" fn(ctx: ffi::MIR_context_t, item: ffi::MIR_item_t),
322 >,
323 resolver: Option<&ImportResolver>,
324 ) {
325 unsafe extern "C-unwind" fn trampoline(
326 data: *mut c_void,
327 name: *const c_char,
328 ) -> *mut c_void {
329 let name = unsafe { CStr::from_ptr(name) };
330 unsafe { (*data.cast::<&ImportResolver>())(name) }
331 }
332
333 // NB. Keep `resolver` alive on stack by taking a reference.
334 let (resolver, arg) = match &resolver {
335 Some(resolver) => (
336 Some(trampoline as _),
337 // NB. This is a pointer to fat reference to dyn Fn.
338 ptr::from_ref(resolver).cast_mut().cast(),
339 ),
340 None => (None, null_mut()),
341 };
342 unsafe { ffi::MIR_link(self.as_raw(), set_interface, resolver, arg) }
343 }
344}
345
346impl Drop for MirContext {
347 fn drop(&mut self) {
348 if std::thread::panicking() {
349 // MIR_finish* may fail. Avoid double-panicking when something already goes wrong.
350 return;
351 }
352
353 if self.func_item.get().is_some() {
354 unsafe { ffi::MIR_finish_func(self.as_raw()) };
355 }
356 if self.module.get().is_some() {
357 unsafe { ffi::MIR_finish_module(self.as_raw()) };
358 }
359 unsafe { ffi::MIR_finish(self.as_raw()) };
360 }
361}
362
363/// A module reference.
364#[derive(Debug, Clone, Copy)]
365pub struct MirModuleRef<'ctx> {
366 module: NonNull<ffi::MIR_module>,
367 _marker: PhantomData<&'ctx MirContext>,
368}
369
370impl MirModuleRef<'_> {
371 unsafe fn from_raw(raw: *mut ffi::MIR_module) -> Self {
372 Self {
373 module: NonNull::new(raw).expect("module must not be null"),
374 _marker: PhantomData,
375 }
376 }
377
378 /// Get the underlying pointer for FFI.
379 #[must_use]
380 pub fn as_raw(&self) -> *mut ffi::MIR_module {
381 self.module.as_ptr()
382 }
383
384 /// Get the name of the module.
385 #[must_use]
386 pub fn name(&self) -> &CStr {
387 unsafe { CStr::from_ptr(self.module.as_ref().name) }
388 }
389
390 /// Get the list of all items inside the module.
391 #[must_use]
392 pub fn get_items(&self) -> Vec<ItemRef<'_>> {
393 let head = unsafe { self.module.as_ref().items.head };
394 std::iter::successors(NonNull::new(head), |item| unsafe {
395 NonNull::new(item.as_ref().item_link.next)
396 })
397 .map(|item| unsafe { ItemRef::from_raw(item.as_ptr()) })
398 .collect()
399 }
400
401 /// Dump the content of the context in a textual representation for human consumption.
402 #[must_use]
403 pub fn dump(&self, ctx: &MirContext) -> String {
404 MemoryFile::with(|file| unsafe {
405 ffi::MIR_output_module(ctx.as_raw(), file, self.as_raw());
406 })
407 .1
408 }
409
410 /// Serialize the module into bytes.
411 ///
412 /// The serialization format is stable across executions, but may not be stable across
413 /// MIR versions.
414 #[cfg(feature = "io")]
415 pub fn serialize(&self, ctx: &MirContext) -> Vec<u8> {
416 let mut buf = Vec::new();
417 unsafe {
418 ffi::MIR_write_module_with_func(
419 ctx.as_raw(),
420 Some(write_byte_callback),
421 self.as_raw(),
422 ptr::from_mut(&mut buf).cast(),
423 );
424 }
425 buf
426 }
427}
428
429/// The currently building MIR module.
430///
431/// There can only be at most one unfinished module for each [`MirContext`].
432/// It is strongly advised to explicitly call [`MirModuleBuilder::finish`] to finish the current
433/// module for clarity and observe any error outside drop impl.
434/// [`MirModuleBuilder`] will automatically finish it on drop.
435#[derive(Debug)]
436#[must_use = "module builder should be correctly finished by finish()"]
437pub struct MirModuleBuilder<'ctx> {
438 ctx: &'ctx MirContext,
439}
440
441impl Drop for MirModuleBuilder<'_> {
442 fn drop(&mut self) {
443 self.ctx.module.take().expect("must be inside a module");
444 unsafe { ffi::MIR_finish_module(self.as_raw_ctx()) };
445 }
446}
447
448impl<'ctx> MirModuleBuilder<'ctx> {
449 /// Explicitly finish the module and return the module reference.
450 ///
451 /// # Panics
452 ///
453 /// Panic from C if the module content is malformed.
454 #[expect(clippy::must_use_candidate, reason = "can be ignored")]
455 pub fn finish(self) -> MirModuleRef<'ctx> {
456 let module = self.ctx.module.get().expect("must be inside a module");
457 drop(self);
458 unsafe { MirModuleRef::from_raw(module.as_ptr()) }
459 }
460
461 fn as_raw_ctx(&self) -> *mut ffi::MIR_context {
462 self.ctx.as_raw()
463 }
464
465 /// Add a new function prototype and return its reference.
466 ///
467 /// # Panics
468 ///
469 /// Panic from C on duplicated names or invalid signature.
470 #[must_use]
471 pub fn add_proto(&self, name: &CStr, rets: &[Ty], args: &[(&CStr, Ty)]) -> ProtoItemRef<'_> {
472 let c_args = args
473 .iter()
474 .map(|(name, ty)| ffi::MIR_var {
475 type_: ty.0,
476 name: name.as_ptr(),
477 // Unused.
478 size: 0,
479 })
480 .collect::<Vec<_>>();
481 let item = unsafe {
482 ItemRef::from_raw(ffi::MIR_new_proto_arr(
483 self.as_raw_ctx(),
484 name.as_ptr(),
485 rets.len(),
486 rets.as_ptr().cast::<ffi::MIR_type_t>().cast_mut(),
487 c_args.len(),
488 c_args.as_ptr().cast_mut(),
489 ))
490 };
491 ProtoItemRef(item)
492 }
493
494 /// Add a new import name and return its reference.
495 ///
496 /// # Panics
497 ///
498 /// Panic from C on duplicated names.
499 #[must_use]
500 pub fn add_import(&self, name: &CStr) -> ImportItemRef<'_> {
501 let item =
502 unsafe { ItemRef::from_raw(ffi::MIR_new_import(self.as_raw_ctx(), name.as_ptr())) };
503 ImportItemRef(item)
504 }
505
506 /// Add a new export name and return its reference.
507 ///
508 /// # Panics
509 ///
510 /// Panic from C on duplicated names.
511 #[expect(clippy::must_use_candidate, reason = "can be ignored")]
512 pub fn add_export(&self, name: &CStr) -> ExportItemRef<'_> {
513 let item =
514 unsafe { ItemRef::from_raw(ffi::MIR_new_export(self.as_raw_ctx(), name.as_ptr())) };
515 ExportItemRef(item)
516 }
517
518 /// Add a new forward declaration and return its reference.
519 ///
520 /// # Panics
521 ///
522 /// Panic from C on duplicated names.
523 #[expect(clippy::must_use_candidate, reason = "can be ignored")]
524 pub fn add_forward(&self, name: &CStr) -> ForwardItemRef<'_> {
525 let item =
526 unsafe { ItemRef::from_raw(ffi::MIR_new_forward(self.as_raw_ctx(), name.as_ptr())) };
527 ForwardItemRef(item)
528 }
529
530 /// Add a new forward declaration and return its reference.
531 ///
532 /// # Panics
533 ///
534 /// Panic from C on duplicated names.
535 #[must_use]
536 pub fn add_data<'a>(&self, name: impl Into<Option<&'a CStr>>, data: &[u8]) -> DataItemRef<'_> {
537 unsafe {
538 DataItemRef(ItemRef::from_raw(ffi::MIR_new_data(
539 self.as_raw_ctx(),
540 name.into().map_or(null(), CStr::as_ptr),
541 Ty::U8.0,
542 data.len(),
543 data.as_ptr().cast(),
544 )))
545 }
546 }
547
548 /// Add a new data referencing another item and return its reference.
549 ///
550 /// The address of `ref_item` after linking plus `disp` is used to initialize the data.
551 ///
552 /// # Panics
553 ///
554 /// Panic from C on duplicated names.
555 #[must_use]
556 pub fn add_ref_data<'a>(
557 &self,
558 name: impl Into<Option<&'a CStr>>,
559 ref_item: ItemRef<'_>,
560 disp: i64,
561 ) -> RefDataItemRef<'_> {
562 unsafe {
563 RefDataItemRef(ItemRef::from_raw(ffi::MIR_new_ref_data(
564 self.as_raw_ctx(),
565 name.into().map_or(null(), CStr::as_ptr),
566 ref_item.as_raw(),
567 disp,
568 )))
569 }
570 }
571
572 /// Add a new data initialized by a function and return its reference.
573 ///
574 /// - Not all MIR functions can be used for expression data. The expression function should
575 /// have only one result, have no arguments, not use any call or any instruction with memory.
576 /// - The expression function is called during linking and its result is used to initialize the
577 /// data.
578 ///
579 /// # Panics
580 ///
581 /// Panic from C on duplicated names or unsupported functions.
582 #[must_use]
583 pub fn add_expr_data<'a>(
584 &self,
585 name: impl Into<Option<&'a CStr>>,
586 expr_func: FuncItemRef<'_>,
587 ) -> ExprDataItemRef<'_> {
588 unsafe {
589 ExprDataItemRef(ItemRef::from_raw(ffi::MIR_new_expr_data(
590 self.as_raw_ctx(),
591 name.into().map_or(null(), CStr::as_ptr),
592 expr_func.as_raw(),
593 )))
594 }
595 }
596
597 /// Add a new data initialized by a label and return its reference.
598 ///
599 /// The addresses defined as `label[-base_label]+disp` where `base_label` can be `None`.
600 /// `lref` can refers for labels the same function (this is checked during module load) and
601 /// there is a warranty label addresses to be defined only at the beginning of the function
602 /// execution.
603 ///
604 /// # Panics
605 ///
606 /// Panic from C on duplicated names.
607 #[must_use]
608 pub fn add_label_ref_data(
609 &self,
610 name: &CStr,
611 label: Label<'_>,
612 base_label: Option<Label<'_>>,
613 disp: i64,
614 ) -> LabelRefDataItemRef<'_> {
615 unsafe {
616 LabelRefDataItemRef(ItemRef::from_raw(ffi::MIR_new_lref_data(
617 self.as_raw_ctx(),
618 name.as_ptr(),
619 label.0,
620 base_label.map_or(null_mut(), |lbl| lbl.0),
621 disp,
622 )))
623 }
624 }
625
626 /// Add a new writable memory segment and return its reference.
627 ///
628 /// # Panics
629 ///
630 /// Panic from C on duplicated names.
631 #[must_use]
632 pub fn add_bss<'a>(&self, name: impl Into<Option<&'a CStr>>, len: usize) -> BssItemRef<'_> {
633 unsafe {
634 BssItemRef(ItemRef::from_raw(ffi::MIR_new_bss(
635 self.as_raw_ctx(),
636 name.into().map_or(null(), CStr::as_ptr),
637 len,
638 )))
639 }
640 }
641
642 /// Add a new function definition and enter into it.
643 ///
644 /// The MIR context is stateful and the previous function must be finished before creating
645 /// another one.
646 /// See more details in [`MirFuncBuilder`].
647 ///
648 /// # Panics
649 ///
650 /// Panic if there is any unfinished function.
651 /// Panic from C on duplicated names or invalid signature.
652 #[must_use]
653 pub fn enter_new_function(
654 &'_ self,
655 name: &CStr,
656 rets: &[Ty],
657 args: &[(&CStr, Ty)],
658 ) -> MirFuncBuilder<'_, 'ctx> {
659 assert!(
660 self.ctx.func_item.get().is_none(),
661 "already inside a function"
662 );
663 let c_args = args
664 .iter()
665 .map(|(name, ty)| ffi::MIR_var {
666 type_: ty.0,
667 name: name.as_ptr(),
668 // Unused.
669 size: 0,
670 })
671 .collect::<Vec<_>>();
672 let func_item = unsafe {
673 ffi::MIR_new_func_arr(
674 self.as_raw_ctx(),
675 name.as_ptr(),
676 rets.len(),
677 rets.as_ptr().cast::<ffi::MIR_type_t>().cast_mut(),
678 c_args.len(),
679 c_args.as_ptr().cast_mut(),
680 )
681 };
682 self.ctx.func_item.set(Some(
683 NonNull::new(func_item).expect("item must not be null"),
684 ));
685 let func = unsafe { NonNull::new((*func_item).u.func).expect("function must not be null") };
686 MirFuncBuilder {
687 func,
688 ctx: self.ctx,
689 _marker: PhantomData,
690 }
691 }
692}
693
694/// The currently building MIR function.
695///
696/// There can only be at most one unfinished function for each [`MirContext`] inside one unfinished
697/// module. It is strongly advised to explicitly call [`MirFuncBuilder::finish`] to finish the
698/// current function for clarity and observe any error outside drop impl.
699/// [`MirFuncBuilder`] will automatically finish it on drop.
700#[derive(Debug)]
701pub struct MirFuncBuilder<'module, 'ctx> {
702 func: NonNull<ffi::MIR_func>,
703 ctx: &'ctx MirContext,
704 _marker: PhantomData<&'module MirModuleRef<'ctx>>,
705}
706
707impl Drop for MirFuncBuilder<'_, '_> {
708 fn drop(&mut self) {
709 self.ctx
710 .func_item
711 .take()
712 .expect("must be inside a function");
713 unsafe { ffi::MIR_finish_func(self.ctx.ctx.as_ptr()) };
714 }
715}
716
717impl<'ctx> MirFuncBuilder<'_, 'ctx> {
718 /// Explicitly finish the function and return the function reference.
719 ///
720 /// # Panics
721 ///
722 /// Panic from C if the function content is malformed.
723 #[expect(clippy::must_use_candidate, reason = "can be ignored")]
724 pub fn finish(self) -> FuncItemRef<'ctx> {
725 let func_item = self.ctx.func_item.get().expect("must be inside a function");
726 drop(self);
727 FuncItemRef(ItemRef(func_item, PhantomData))
728 }
729
730 /// Get the virtual register or given name.
731 ///
732 /// Function parameters are represented as pre-defined virtual registers of same names.
733 #[must_use]
734 pub fn get_reg(&self, name: &CStr) -> Reg {
735 let reg = unsafe { ffi::MIR_reg(self.ctx.ctx.as_ptr(), name.as_ptr(), self.func.as_ptr()) };
736 Reg(reg)
737 }
738
739 /// Create a new virtual register.
740 #[must_use]
741 pub fn new_local_reg(&self, name: &CStr, ty: Ty) -> Reg {
742 let reg = unsafe {
743 ffi::MIR_new_func_reg(
744 self.ctx.ctx.as_ptr(),
745 self.func.as_ptr(),
746 ty.0,
747 name.as_ptr(),
748 )
749 };
750 Reg(reg)
751 }
752
753 /// Create a new unbound label.
754 ///
755 /// The label must be inserted later via [`InsnBuilder::label`].
756 #[must_use]
757 pub fn new_label(&self) -> Label<'_> {
758 let insn = unsafe { ffi::MIR_new_label(self.ctx.ctx.as_ptr()) };
759 Label(insn, PhantomData)
760 }
761
762 /// Append a new instruction to the function.
763 pub fn ins(&self) -> FuncInstBuilder<'_, 'ctx> {
764 FuncInstBuilder {
765 ctx: self.ctx,
766 _marker: PhantomData,
767 }
768 }
769}
770
771/// The instruction appender to an function.
772///
773/// See [`InsnBuilder`] for all instructions.
774#[derive(Debug)]
775#[must_use = "FuncInstBuilder does nothing unless calling an method"]
776pub struct FuncInstBuilder<'func, 'ctx> {
777 ctx: &'ctx MirContext,
778 _marker: PhantomData<&'func MirFuncBuilder<'func, 'ctx>>,
779}
780
781unsafe impl<'func> InsnBuilderBase<'func> for FuncInstBuilder<'func, '_> {
782 fn get_raw_ctx(&self) -> ffi::MIR_context_t {
783 self.ctx.ctx.as_ptr()
784 }
785
786 unsafe fn insert(self, insn: ffi::MIR_insn_t) {
787 unsafe {
788 ffi::MIR_append_insn(
789 self.ctx.as_raw(),
790 self.ctx
791 .func_item
792 .get()
793 .expect("must be inside a function")
794 .as_ptr()
795 .cast::<ffi::MIR_item>(),
796 insn,
797 );
798 }
799 }
800}