mir/
types.rs

1use std::ffi::{CStr, c_char};
2use std::marker::PhantomData;
3use std::ptr::{NonNull, null_mut};
4use std::{fmt, ops};
5
6use paste::paste;
7use smallvec::SmallVec;
8
9use crate::{MemoryFile, MirContext, ffi};
10
11/// Type of a (fatal) error from C.
12#[repr(transparent)]
13pub(crate) struct ErrorType(pub(crate) ffi::MIR_error_type);
14
15macro_rules! impl_error_type_variants {
16    ($($var:ident),* $(,)?) => {
17        impl fmt::Display for ErrorType {
18            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19                match self.0 {
20                    $(paste!(ffi:: [<MIR_ $var _error>]) => concat!("MIR_", stringify!($var), "_error"),)*
21                    raw => return raw.fmt(f),
22                }
23                .fmt(f)
24            }
25        }
26    };
27}
28
29impl_error_type_variants! {
30    no,
31    syntax,
32    binary_io,
33    alloc,
34    finish,
35    no_module,
36    nested_module,
37    no_func,
38    func,
39    vararg_func,
40    nested_func,
41    wrong_param_value,
42    hard_reg,
43    reserved_name,
44    import_export,
45    undeclared_func_reg,
46    repeated_decl,
47    reg_type,
48    wrong_type,
49    unique_reg,
50    undeclared_op_ref,
51    ops_num,
52    call_op,
53    unspec_op,
54    wrong_lref,
55    ret,
56    op_mode,
57    out_op,
58    invalid_insn,
59    ctx_change,
60}
61
62/// Type of virtual registers.
63#[derive(Clone, Copy, PartialEq, Eq)]
64#[repr(transparent)]
65pub struct Ty(pub(crate) ffi::MIR_type_t);
66
67macro_rules! impl_ty_variants {
68    ($($var:ident),* $(,)?) => {
69        impl Ty {
70            $(
71                #[doc = concat!("`MIR_T_", stringify!($var), "`")]
72                pub const $var: Self = Self(paste!(ffi::[<MIR_T_ $var>]));
73            )*
74        }
75
76        impl fmt::Debug for Ty {
77            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78                let s = match *self {
79                    $(Self::$var => concat!("Ty(MIR_T_", stringify!($var), ")"),)*
80                    Self(raw) => return f.debug_tuple("Ty").field(&raw).finish(),
81                };
82                f.write_str(s)
83            }
84        }
85    };
86}
87
88impl_ty_variants! {
89    I8, U8, I16, U16, I32, U32, I64, U64,
90    F, D, LD, P,
91    BLK, RBLK, UNDEF, BOUND,
92}
93
94#[test]
95fn ty_debug() {
96    assert_eq!(format!("{:?}", Ty::I64), "Ty(MIR_T_I64)");
97    assert_eq!(format!("{:?}", Ty(99)), "Ty(99)");
98}
99
100/// Virtual register identifier.
101#[derive(Debug, Clone, Copy)]
102#[repr(transparent)]
103pub struct Reg(pub(crate) ffi::MIR_reg_t);
104
105/// Type of items.
106#[derive(Clone, Copy, PartialEq, Eq)]
107#[repr(transparent)]
108pub struct ItemType(pub(crate) ffi::MIR_item_type_t);
109
110macro_rules! impl_item_type_variants {
111    ($($var:ident),* $(,)?) => {
112        impl ItemType {
113            $(
114                #[doc = paste!(concat!("`MIR_", stringify!([<$var:lower>]), "_item`"))]
115                pub const $var: Self = Self(paste!(ffi::[<MIR_ $var:lower _item>]));
116            )*
117        }
118
119        impl fmt::Debug for ItemType {
120            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121                let s = match *self {
122                    $(Self::$var => paste!(concat!("ItemType(MIR_", stringify!($var:lower), "_item)")),)*
123                    Self(raw) => return f.debug_tuple("ItemType").field(&raw).finish(),
124                };
125                f.write_str(s)
126            }
127        }
128    };
129}
130
131impl_item_type_variants! {
132    FUNC, PROTO,
133    IMPORT, EXPORT, FORWARD,
134    DATA, REF_DATA, LREF_DATA, EXPR_DATA,
135    BSS,
136}
137
138/// Reference to an item.
139#[derive(Clone, Copy)]
140#[repr(transparent)]
141pub struct ItemRef<'a>(
142    pub(crate) NonNull<ffi::MIR_item>,
143    pub(crate) PhantomData<&'a ffi::MIR_item>,
144);
145
146impl ItemRef<'_> {
147    pub(crate) unsafe fn from_raw(raw: *mut ffi::MIR_item) -> Self {
148        let raw = NonNull::new(raw).expect("item must not be null");
149        Self(raw, PhantomData)
150    }
151
152    /// Get the underlying pointer for FFI.
153    #[must_use]
154    pub fn as_raw(&self) -> *mut ffi::MIR_item {
155        self.0.as_ptr()
156    }
157
158    /// Dump the content in a textual representation for human consumption.
159    #[must_use]
160    pub fn dump(&self, ctx: &MirContext) -> String {
161        MemoryFile::with(|file| unsafe { ffi::MIR_output_item(ctx.as_raw(), file, self.as_raw()) })
162            .1
163    }
164
165    /// Get the item type.
166    #[must_use]
167    pub fn type_(&self) -> ItemType {
168        unsafe { ItemType(self.0.as_ref().item_type) }
169    }
170
171    /// Get the name of the item.
172    #[must_use]
173    pub fn name(&self) -> Option<&CStr> {
174        let item = unsafe { self.0.as_ref() };
175        let data = &item.u;
176        let ptr = unsafe {
177            match item.item_type {
178                ffi::MIR_func_item => (*data.func.cast::<FuncItemData>()).0.name,
179                ffi::MIR_proto_item => (*data.func.cast::<ProtoItemData>()).0.name,
180                ffi::MIR_import_item => data.import_id,
181                ffi::MIR_export_item => data.export_id,
182                ffi::MIR_forward_item => data.forward_id,
183                ffi::MIR_data_item => (*data.data.cast::<DataItemData>()).0.name,
184                ffi::MIR_ref_data_item => (*data.ref_data.cast::<RefDataItemData>()).0.name,
185                ffi::MIR_lref_data_item => (*data.lref_data.cast::<LabelRefDataItemData>()).0.name,
186                ffi::MIR_expr_data_item => (*data.expr_data.cast::<ExprDataItemData>()).0.name,
187                ffi::MIR_bss_item => (*data.bss.cast::<BssItemData>()).0.name,
188                _ => return None,
189            }
190        };
191        if ptr.is_null() {
192            None
193        } else {
194            unsafe { Some(CStr::from_ptr(ptr)) }
195        }
196    }
197}
198
199impl<'a> From<ItemRef<'a>> for Operand<'a> {
200    fn from(item: ItemRef<'a>) -> Self {
201        Operand {
202            op: unsafe { ffi::MIR_new_ref_op(null_mut(), item.as_raw()) },
203            _marker: PhantomData,
204        }
205    }
206}
207
208impl fmt::Debug for ItemRef<'_> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        let mut fs = f.debug_struct("ItemRef");
211        let item = unsafe { self.0.as_ref() };
212        fs.field("ptr", &std::ptr::from_ref(self))
213            .field("module", &item.module)
214            .field("addr", &item.addr)
215            .field("type", &self.type_());
216        let data = &item.u;
217        let mut cb = |data| {
218            fs.field("u", data);
219        };
220        match item.item_type {
221            ffi::MIR_func_item => cb(unsafe { &*data.func.cast::<FuncItemData>() }),
222            ffi::MIR_proto_item => cb(unsafe { &*data.func.cast::<ProtoItemData>() }),
223            ffi::MIR_import_item => cb(&DebugImportLikeItemData(
224                "ImportItemData",
225                "import_id",
226                unsafe { data.import_id },
227            )),
228            ffi::MIR_export_item => cb(&DebugImportLikeItemData(
229                "ExportItemData",
230                "export_id",
231                unsafe { data.export_id },
232            )),
233            ffi::MIR_forward_item => cb(&DebugImportLikeItemData(
234                "ForwardItemData",
235                "forward_id",
236                unsafe { data.forward_id },
237            )),
238            ffi::MIR_data_item => cb(unsafe { &*data.data.cast::<DataItemData>() }),
239            ffi::MIR_ref_data_item => cb(unsafe { &*data.ref_data.cast::<RefDataItemData>() }),
240            ffi::MIR_lref_data_item => {
241                cb(unsafe { &*data.lref_data.cast::<LabelRefDataItemData>() });
242            }
243            ffi::MIR_expr_data_item => cb(unsafe { &*data.expr_data.cast::<ExprDataItemData>() }),
244            ffi::MIR_bss_item => cb(unsafe { &*data.bss.cast::<BssItemData>() }),
245            _ => {}
246        }
247        fs.finish_non_exhaustive()
248    }
249}
250
251/// Item downcast failure.
252#[derive(Debug, Clone)]
253pub struct ItemRefDowncastError(());
254
255impl fmt::Display for ItemRefDowncastError {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        f.write_str("item type mismatch")
258    }
259}
260
261impl std::error::Error for ItemRefDowncastError {}
262
263macro_rules! def_item_ref_variant {
264    ($name:ident, $item_type:expr, $doc:literal) => {
265        /// Reference to a
266        #[doc = $doc]
267        /// item.
268        #[derive(Debug, Clone, Copy)]
269        #[repr(transparent)]
270        pub struct $name<'a>(pub(crate) ItemRef<'a>);
271
272        impl<'a> ops::Deref for $name<'a> {
273            type Target = ItemRef<'a>;
274
275            fn deref(&self) -> &Self::Target {
276                &self.0
277            }
278        }
279
280        impl<'a> From<$name<'a>> for Operand<'a> {
281            fn from(item: $name<'a>) -> Self {
282                item.0.into()
283            }
284        }
285
286        impl<'a> TryFrom<ItemRef<'a>> for $name<'a> {
287            type Error = ItemRefDowncastError;
288            fn try_from(item: ItemRef<'a>) -> Result<Self, Self::Error> {
289                if unsafe { item.0.as_ref().item_type } == $item_type {
290                    Ok(Self(item))
291                } else {
292                    Err(ItemRefDowncastError(()))
293                }
294            }
295        }
296    };
297}
298
299def_item_ref_variant!(FuncItemRef, ffi::MIR_func_item, "function");
300
301impl FuncItemRef<'_> {
302    /// # Safety
303    /// The returned reference is invalidated if any MIR functions is called.
304    pub(crate) unsafe fn data(&self) -> &ffi::MIR_func {
305        unsafe { &*self.0.0.as_ref().u.func }
306    }
307}
308
309#[repr(transparent)]
310struct FuncItemData(ffi::MIR_func);
311
312impl fmt::Debug for FuncItemData {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        let u = &self.0;
315        f.debug_struct("FuncItemData")
316            .field("name", &unsafe { CStr::from_ptr(u.name) })
317            .field("func_item", &u.func_item)
318            .field("original_vars_num", &u.original_insns)
319            .field("nres", &u.nres)
320            .field("nargs", &u.nargs)
321            .field("res_types", &unsafe {
322                std::slice::from_raw_parts(u.res_types.cast::<Ty>(), u.nres as usize)
323            })
324            .field("varargs_p", &(u.vararg_p != 0))
325            .field("expr_p", &(u.expr_p != 0))
326            .field("jret_p", &(u.jret_p != 0))
327            .field("vars", &DebugVars(u.vars))
328            .field("global_vars", &DebugVars(u.global_vars))
329            .finish_non_exhaustive()
330    }
331}
332
333#[repr(transparent)]
334struct DebugVar(ffi::MIR_var);
335
336impl fmt::Debug for DebugVar {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        f.debug_struct("Var")
339            .field("type", &Ty(self.0.type_))
340            .field("name", &unsafe { CStr::from_ptr(self.0.name) })
341            // .field("size", &self.0.size) // Contains garbage?
342            .finish_non_exhaustive()
343    }
344}
345
346struct DebugVars(*mut ffi::VARR_MIR_var_t);
347
348impl fmt::Debug for DebugVars {
349    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350        let vars = if self.0.is_null() {
351            &[]
352        } else {
353            unsafe {
354                let varr = &*self.0;
355                std::slice::from_raw_parts(varr.varr.cast::<DebugVar>().cast_const(), varr.els_num)
356            }
357        };
358        vars.fmt(f)
359    }
360}
361
362def_item_ref_variant!(ProtoItemRef, ffi::MIR_proto_item, "prototype");
363
364#[repr(transparent)]
365struct ProtoItemData(ffi::MIR_proto);
366
367impl fmt::Debug for ProtoItemData {
368    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369        let u = &self.0;
370        f.debug_struct("ProtoItemData")
371            .field("name", &unsafe { CStr::from_ptr(u.name) })
372            .field("nres", &u.nres)
373            .field("res_types", &unsafe {
374                std::slice::from_raw_parts(u.res_types.cast::<Ty>(), u.nres as usize)
375            })
376            .finish_non_exhaustive()
377    }
378}
379
380def_item_ref_variant!(ImportItemRef, ffi::MIR_import_item, "import");
381def_item_ref_variant!(ExportItemRef, ffi::MIR_export_item, "export");
382def_item_ref_variant!(ForwardItemRef, ffi::MIR_forward_item, "forward declaration");
383
384struct DebugImportLikeItemData(&'static str, &'static str, *const c_char);
385
386impl fmt::Debug for DebugImportLikeItemData {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        f.debug_struct(self.0)
389            .field(self.1, &unsafe { CStr::from_ptr(self.2) })
390            .finish()
391    }
392}
393
394def_item_ref_variant!(DataItemRef, ffi::MIR_data_item, "data");
395
396#[repr(transparent)]
397struct DataItemData(ffi::MIR_data);
398
399impl fmt::Debug for DataItemData {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        let u = &self.0;
402        f.debug_struct("DataItemData")
403            .field("name", &unsafe { CStr::from_ptr(u.name) })
404            .field("el_type", &Ty(u.el_type))
405            .field("nel", &u.nel)
406            .finish_non_exhaustive()
407    }
408}
409
410def_item_ref_variant!(RefDataItemRef, ffi::MIR_ref_data_item, "referential data");
411
412#[repr(transparent)]
413struct RefDataItemData(ffi::MIR_ref_data);
414
415impl fmt::Debug for RefDataItemData {
416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417        let u = &self.0;
418        f.debug_struct("RefDataItemData")
419            .field("name", &unsafe { CStr::from_ptr(u.name) })
420            .field("ref_item", &unsafe { ItemRef::from_raw(u.ref_item) })
421            .field("disp", &u.disp)
422            .field("load_addr", &u.load_addr)
423            .finish()
424    }
425}
426
427def_item_ref_variant!(LabelRefDataItemRef, ffi::MIR_lref_data_item, "label data");
428
429#[repr(transparent)]
430struct LabelRefDataItemData(ffi::MIR_lref_data);
431
432impl fmt::Debug for LabelRefDataItemData {
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        let u = &self.0;
435        f.debug_struct("LabelRefDataItemData")
436            .field("name", &unsafe { CStr::from_ptr(u.name) })
437            .field("disp", &u.disp)
438            .field("load_addr", &u.load_addr)
439            .finish()
440    }
441}
442
443def_item_ref_variant!(ExprDataItemRef, ffi::MIR_expr_data_item, "computed data");
444
445#[repr(transparent)]
446struct ExprDataItemData(ffi::MIR_expr_data);
447
448impl fmt::Debug for ExprDataItemData {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        let u = &self.0;
451        f.debug_struct("ExprItemData")
452            .field("name", &unsafe { CStr::from_ptr(u.name) })
453            .field("expr_item", &unsafe { ItemRef::from_raw(u.expr_item) })
454            .finish_non_exhaustive()
455    }
456}
457
458def_item_ref_variant!(BssItemRef, ffi::MIR_bss_item, "writable memory segment");
459
460#[repr(transparent)]
461struct BssItemData(ffi::MIR_bss);
462
463impl fmt::Debug for BssItemData {
464    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465        let u = &self.0;
466        f.debug_struct("BssItemData")
467            .field("name", &unsafe { CStr::from_ptr(u.name) })
468            .field("len", &u.len)
469            .finish()
470    }
471}
472
473/// Operand of instructions.
474#[derive(Clone, Copy)]
475#[repr(transparent)]
476pub struct Operand<'a> {
477    pub(crate) op: ffi::MIR_op_t,
478    pub(crate) _marker: PhantomData<&'a ()>,
479}
480
481impl fmt::Debug for Operand<'_> {
482    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483        let mut s = f.debug_struct("Operand");
484        unsafe {
485            match ffi::MIR_op_mode_t::from(self.op.mode) {
486                ffi::MIR_OP_REG => s.field("reg", &self.op.u.reg),
487                ffi::MIR_OP_VAR => s.field("var", &self.op.u.var),
488                ffi::MIR_OP_INT => s.field("int", &self.op.u.i),
489                ffi::MIR_OP_UINT => s.field("uint", &self.op.u.u),
490                ffi::MIR_OP_FLOAT => s.field("float", &self.op.u.f),
491                ffi::MIR_OP_DOUBLE => s.field("double", &self.op.u.d),
492                ffi::MIR_OP_REF => s.field("ref", &ItemRef::from_raw(self.op.u.ref_)),
493                ffi::MIR_OP_MEM => s.field("mem", &self.op.u.mem),
494                ffi::MIR_OP_LABEL => s.field("label", &..),
495                // Not implemented yet.
496                // ffi::MIR_OP_STR
497                // ffi::MIR_OP_VAR_MEM
498                // ffi::MIR_OP_BOUND
499                _ => return s.finish_non_exhaustive(),
500            }
501            .finish()
502        }
503    }
504}
505
506/// Convertible to [`Operand`].
507pub trait IntoOperand<'a>: Into<Operand<'a>> {}
508
509/// Convertible to [`Operand`], but can only be used in destination place.
510pub trait IntoOutOperand<'a>: IntoOperand<'a> {}
511
512impl<'a, T> IntoOperand<'a> for T where Operand<'a>: From<T> {}
513
514impl IntoOutOperand<'_> for Reg {}
515impl From<Reg> for Operand<'_> {
516    fn from(reg: Reg) -> Self {
517        Self {
518            op: unsafe { ffi::MIR_new_reg_op(null_mut(), reg.0) },
519            _marker: PhantomData,
520        }
521    }
522}
523
524/// Label identifier.
525#[derive(Debug, Clone, Copy)]
526#[repr(transparent)]
527pub struct Label<'a>(pub(crate) ffi::MIR_label_t, pub(crate) PhantomData<&'a ()>);
528
529impl<'a> From<Label<'a>> for Operand<'a> {
530    fn from(label: Label<'a>) -> Self {
531        Self {
532            op: unsafe { ffi::MIR_new_label_op(null_mut(), label.0) },
533            _marker: PhantomData,
534        }
535    }
536}
537
538/// Memory operand.
539///
540/// It represents the data located at address `(disp + base_reg + index_reg * scale)`,
541/// where `disp` is a constant and `scale` is either 1, 2, 4 or 8.
542#[derive(Debug, Clone, Copy)]
543pub struct MemOp {
544    ty: Ty,
545    disp: i64,
546    base: Reg,
547    index: Reg,
548    scale: u8,
549}
550
551impl MemOp {
552    /// Create a new memory operand with type `ty`, based on register `base`.
553    #[must_use]
554    pub fn new_base(ty: Ty, base: Reg) -> Self {
555        Self {
556            ty,
557            disp: 0,
558            base,
559            index: Reg(0),
560            scale: 1,
561        }
562    }
563
564    /// Set the displacement and return updated operand.
565    #[must_use]
566    pub fn disp(self, disp: i64) -> Self {
567        Self { disp, ..self }
568    }
569
570    /// Set the index register and return updated operand.
571    #[must_use]
572    pub fn index(self, index: Reg) -> Self {
573        Self { index, ..self }
574    }
575
576    /// Set the scale and return updated operand.
577    ///
578    /// # Panics
579    ///
580    /// Panics if `scale` is not 1, 2, 4 or 8.
581    #[must_use]
582    pub fn scale(self, scale: u8) -> Self {
583        assert!(matches!(scale, 1 | 2 | 4 | 8), "scale must be 1, 2, 4 or 8");
584        Self { scale, ..self }
585    }
586}
587
588impl IntoOutOperand<'_> for MemOp {}
589impl From<MemOp> for Operand<'_> {
590    fn from(mem: MemOp) -> Self {
591        Self {
592            op: unsafe {
593                ffi::MIR_new_mem_op(
594                    null_mut(),
595                    mem.ty.0,
596                    mem.disp,
597                    mem.base.0,
598                    mem.index.0,
599                    mem.scale,
600                )
601            },
602            _marker: PhantomData,
603        }
604    }
605}
606
607macro_rules! impl_literal_into_op {
608    ($($ty:ident, $func:ident;)*) => {
609        $(
610            impl From<$ty> for Operand<'_> {
611                fn from(v: $ty) -> Self {
612                    Self {
613                        op: unsafe { ffi::$func(null_mut(), v) },
614                        _marker: PhantomData,
615                    }
616                }
617            }
618        )*
619    };
620}
621
622impl_literal_into_op! {
623    i64, MIR_new_int_op;
624    u64, MIR_new_uint_op;
625    f32, MIR_new_float_op;
626    f64, MIR_new_double_op;
627}
628
629macro_rules! def_simple_insn {
630    (__impl $args:tt $name:ident) => {
631        def_simple_insn!(__impl $args $name paste!(ffi::[<MIR_ $name:upper>]));
632    };
633    (__impl (dst $(, $src:ident)*) $name:ident $code:expr) => {
634        fn $name<'o>(self, dst: impl IntoOutOperand<'o>, $($src: impl IntoOperand<'o>),*) {
635            build_insn(self, $code, [dst.into(), $($src.into()),*]);
636        }
637    };
638    (__impl (_ $(, $src:ident)*) $name:ident $code:expr) => {
639        fn $name<'o>(self, $($src: impl IntoOperand<'o>),*) {
640            build_insn(self, $code, [$($src.into()),*]);
641        }
642    };
643    (
644        $args:tt;
645        $($name:ident $(($code:expr))?),*
646        $(,)?
647    ) => {
648        $(def_simple_insn!(__impl $args $name $($code)?);)*
649    };
650}
651
652macro_rules! def_jump_insn {
653    (__impl (label $(, $src:ident)*) $name:ident) => {
654        #[allow(clippy::needless_lifetimes)]
655        fn $name<'o>(self, label: Label<'o>, $($src: impl IntoOperand<'o>),*) {
656            build_insn(self, paste!(ffi::[<MIR_ $name:upper>]), [label.into(), $($src.into()),*]);
657        }
658    };
659    (
660        $args:tt;
661        $($name:ident $(($code:expr))?),*
662        $(,)?
663    ) => {
664        $(def_jump_insn!(__impl $args $name $($code)?);)*
665    };
666}
667
668macro_rules! def_call_insn {
669    ($($name:ident),* $(,)?) => {
670        $(
671            fn $name<'o>(
672                self,
673                proto: ProtoItemRef<'o>,
674                func: impl IntoOperand<'o>,
675                results: impl IntoIterator<Item = Operand<'o>>,
676                args: impl IntoIterator<Item = Operand<'o>>,
677            ) {
678                build_insn(
679                    self,
680                    paste!(ffi::[<MIR_ $name:upper>]),
681                    [proto.into(), func.into()]
682                        .into_iter()
683                        .chain(results)
684                        .chain(args),
685                );
686            }
687        )*
688    };
689}
690
691/// !! Not a public API !!
692///
693/// # Safety
694/// `get_raw_ctx` must return a valid MIR context.
695#[doc(hidden)]
696pub unsafe trait InsnBuilderBase<'module>: Sized {
697    fn get_raw_ctx(&self) -> ffi::MIR_context_t;
698    /// # Safety
699    /// `insn` must be a valid instruction that is not inserted anywhere.
700    unsafe fn insert(self, insn: ffi::MIR_insn_t);
701}
702
703impl<'module, T: InsnBuilderBase<'module>> InsnBuilder<'module> for T {}
704
705/// The instruction builder.
706///
707/// See detail instruction semantics in [MIR documentation][upstream-docs].
708///
709/// [upstream-docs]: https://github.com/vnmakarov/mir/blob/v1.0.0/MIR.md#mir-insns
710///
711/// # Example
712///
713/// ```
714/// # fn codegen(f: &mir::MirFuncBuilder<'_, '_>, [sum, a, b]: [mir::Reg; 3]) {
715/// use mir::InsnBuilder as _;
716/// // let f: &MirFuncBuilder<'_, '_>;
717/// // let a, b, c: Reg;
718/// f.ins().add(sum, a, b);
719/// f.ins().ret(sum);
720/// # }
721/// ```
722#[allow(missing_docs)]
723pub trait InsnBuilder<'module>: InsnBuilderBase<'module> {
724    // Mostly follows the order in mir.h.
725
726    // Unary ops.
727    def_simple_insn! {
728        (dst, src);
729
730        mov, fmov, dmov, // ldmov
731
732        ext8, ext16, ext32, uext8, uext16, uext32,
733
734        i2f, i2d, // i2ld
735        ui2f, ui2d, // ui2ld
736        f2i, d2i, // ld2i
737        f2d, d2f, // f2ld, d2ld, ld2f, ld2d
738
739        neg, negs, fneg, dneg, // ldneg
740
741        addr, addr8, addr16, addr32,
742    }
743
744    // Binary ops.
745    def_simple_insn! {
746        (dst, a, b);
747
748        add, adds, fadd, dadd, // ldadd
749        sub, subs, fsub, dsub, // ldsub
750        mul, muls, fmul, dmul, // ldmul
751        div, divs, udiv, udivs, fdiv, ddiv, // lddiv
752        mod_(ffi::MIR_MOD), mods, umod, umods,
753
754        and, ands, or, ors, xor, xors,
755        lsh, lshs, rsh, rshs, ursh, urshs,
756
757        eq, eqs, feq, deq, // ldeq
758        ne, nes, fne, dne, // ldne
759        lt, lts, ult, ults, flt, dlt, // ldlt
760        le, les, ule, ules, fle, dle, // ldle
761        gt, gts, ugt, ugts, fgt, dgt, // ldgt
762        ge, ges, uge, uges, fge, dge, // ldge
763
764        addo, addos, subo, subos, mulo, mulos, umulo, umulos,
765    }
766
767    // Jumps.
768    def_jump_insn!((label); jmp, bo, bno, ubo, ubno);
769    def_simple_insn!((_, label_op); jmpi);
770    def_jump_insn!((label, v); bt, bts, bf, bfs);
771    def_jump_insn! {
772        (label, a, b);
773        beq, beqs, fbeq, dbeq, // ldbeq
774        bne, bnes, fbne, dbne, // ldbne
775        blt, blts, ublt, ublts, fblt, dblt, // ldblt
776        ble, bles, uble, ubles, fble, dble, // ldble
777        bgt, bgts, ubgt, ubgts, fbgt, dbgt, // ldbgt
778        bge, bges, ubge, ubges, fbge, dbge, // ldbge
779    }
780    fn laddr<'o>(self, dst: impl IntoOutOperand<'o>, label: Label<'_>) {
781        build_insn(self, ffi::MIR_LADDR, [dst.into(), label.into()]);
782    }
783
784    // Call and return.
785    def_call_insn!(call, inline, jcall);
786    def_simple_insn!((_, v); ret, jret);
787
788    // Function frame.
789    def_simple_insn!((dst, len); alloca);
790    def_simple_insn!((_, va_list); va_start, va_end);
791    def_simple_insn!((dst, va_list, size, block_type); va_block_arg);
792    fn va_arg<'o>(self, dst: impl IntoOutOperand<'o>, va_list: impl IntoOperand<'o>, mem: MemOp) {
793        build_insn(
794            self,
795            ffi::MIR_VA_ARG,
796            [dst.into(), va_list.into(), mem.into()],
797        );
798    }
799
800    // Label.
801    /// Bind (insert) a previously unbound label to the current location.
802    fn label(self, label: Label<'_>) {
803        unsafe { self.insert(label.0) };
804    }
805    /// Create a new label on the current location (immediately insert).
806    fn new_label(self) -> Label<'module> {
807        let insn = unsafe { ffi::MIR_new_label(self.get_raw_ctx()) };
808        unsafe { self.insert(insn) };
809        Label(insn, PhantomData)
810    }
811}
812
813fn build_insn<'module, 'o>(
814    this: impl InsnBuilderBase<'module>,
815    code: mir_sys::MIR_insn_code_t,
816    ops: impl IntoIterator<Item = Operand<'o>>,
817) {
818    let ctx = this.get_raw_ctx();
819    let ops = ops.into_iter().collect::<SmallVec<[Operand; 6]>>();
820    let insn = unsafe {
821        ffi::MIR_new_insn_arr(
822            ctx,
823            code,
824            ops.len(),
825            ops.as_ptr().cast::<ffi::MIR_op_t>().cast_mut(),
826        )
827    };
828    unsafe { this.insert(insn) };
829}